diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2018-05-06 07:24:19 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2018-05-06 07:24:19 +0000 |
commit | 5709ccd0251e4806cf826a1472c6292ebb3a9790 (patch) | |
tree | c1dea294b0305d7414dc5ea08f86bc34be9ac2cd | |
parent | 8ff139ee3ad0de6f109e8ed10da84756858dae87 (diff) | |
parent | 7f9b6fbeece0867c8f027fb5cafafab25cc4f5c6 (diff) | |
download | build_soong-5709ccd0251e4806cf826a1472c6292ebb3a9790.tar.gz build_soong-5709ccd0251e4806cf826a1472c6292ebb3a9790.tar.bz2 build_soong-5709ccd0251e4806cf826a1472c6292ebb3a9790.zip |
Snap for 4765094 from 7f9b6fbeece0867c8f027fb5cafafab25cc4f5c6 to pi-release
Change-Id: I309dd62ddbb35e39810f36e4545a7eee6d384daa
30 files changed, 1840 insertions, 446 deletions
@@ -219,6 +219,7 @@ bootstrap_go_package { srcs: [ "java/aapt2.go", "java/aar.go", + "java/android_resources.go", "java/androidmk.go", "java/app_builder.go", "java/app.go", @@ -229,8 +230,9 @@ bootstrap_go_package { "java/genrule.go", "java/jacoco.go", "java/java.go", + "java/java_resources.go", "java/proto.go", - "java/resources.go", + "java/support_libraries.go", "java/system_modules.go", ], testSrcs: [ diff --git a/androidmk/cmd/androidmk/android.go b/androidmk/cmd/androidmk/android.go index 5196754e..5cb4869c 100644 --- a/androidmk/cmd/androidmk/android.go +++ b/androidmk/cmd/androidmk/android.go @@ -64,6 +64,9 @@ var rewriteProperties = map[string](func(variableAssignmentContext) error){ "LOCAL_MODULE_SUFFIX": skip, // TODO "LOCAL_PATH": skip, // Nothing to do, except maybe avoid the "./" in paths? "LOCAL_PRELINK_MODULE": skip, // Already phased out + "LOCAL_BUILT_MODULE_STEM": skip, + "LOCAL_USE_AAPT2": skip, // Always enabled in Soong + "LOCAL_JAR_EXCLUDE_FILES": skip, // Soong never excludes files from jars } // adds a group of properties all having the same type @@ -94,6 +97,7 @@ func init() { "LOCAL_NOTICE_FILE": "notice", "LOCAL_JAVA_LANGUAGE_VERSION": "java_version", "LOCAL_INSTRUMENTATION_FOR": "instrumentation_for", + "LOCAL_MANIFEST_FILE": "manifest", "LOCAL_DEX_PREOPT_PROFILE_CLASS_LISTING": "dex_preopt.profile", }) @@ -128,6 +132,7 @@ func init() { "LOCAL_RENDERSCRIPT_FLAGS": "renderscript.flags", "LOCAL_JAVA_RESOURCE_DIRS": "java_resource_dirs", + "LOCAL_RESOURCE_DIR": "resource_dirs", "LOCAL_JAVACFLAGS": "javacflags", "LOCAL_ERROR_PRONE_FLAGS": "errorprone.javacflags", "LOCAL_DX_FLAGS": "dxflags", @@ -142,7 +147,13 @@ func init() { "LOCAL_PROGUARD_FLAGS": "optimize.proguard_flags", "LOCAL_PROGUARD_FLAG_FILES": "optimize.proguard_flag_files", + + // These will be rewritten to libs/static_libs by bpfix, after their presence is used to convert + // java_library_static to android_library. + "LOCAL_SHARED_ANDROID_LIBRARIES": "android_libs", + "LOCAL_STATIC_ANDROID_LIBRARIES": "android_static_libs", }) + addStandardProperties(bpparser.BoolType, map[string]string{ // Bool properties @@ -259,7 +270,7 @@ func classifyLocalOrGlobalPath(value bpparser.Expression) (string, bpparser.Expr } case *bpparser.Operator: if v.Type() != bpparser.StringType { - return "", nil, fmt.Errorf("classifyLocalOrGlobalPath expected a string, got %s", value.Type) + return "", nil, fmt.Errorf("classifyLocalOrGlobalPath expected a string, got %s", v.Type()) } if v.Operator != '+' { @@ -290,7 +301,7 @@ func classifyLocalOrGlobalPath(value bpparser.Expression) (string, bpparser.Expr case *bpparser.String: return "global", value, nil default: - return "", nil, fmt.Errorf("classifyLocalOrGlobalPath expected a string, got %s", value.Type) + return "", nil, fmt.Errorf("classifyLocalOrGlobalPath expected a string, got %s", v.Type()) } } diff --git a/androidmk/cmd/androidmk/androidmk.go b/androidmk/cmd/androidmk/androidmk.go index 6e0b474b..b6a973c0 100644 --- a/androidmk/cmd/androidmk/androidmk.go +++ b/androidmk/cmd/androidmk/androidmk.go @@ -239,7 +239,8 @@ func convertFile(filename string, buffer *bytes.Buffer) (string, []error) { } // check for common supported but undesirable structures and clean them up - err := bpfix.FixTree(tree, bpfix.NewFixRequest().AddAll()) + fixer := bpfix.NewFixer(tree) + tree, err := fixer.Fix(bpfix.NewFixRequest().AddAll()) if err != nil { return "", []error{err} } diff --git a/androidmk/cmd/androidmk/androidmk_test.go b/androidmk/cmd/androidmk/androidmk_test.go index 45df1a57..edf3d42f 100644 --- a/androidmk/cmd/androidmk/androidmk_test.go +++ b/androidmk/cmd/androidmk/androidmk_test.go @@ -17,11 +17,10 @@ package main import ( "bytes" "fmt" - "os" "strings" "testing" - bpparser "github.com/google/blueprint/parser" + "android/soong/bpfix/bpfix" ) var testCases = []struct { @@ -498,6 +497,7 @@ include $(call all-makefiles-under,$(LOCAL_PATH)) include $(CLEAR_VARS) LOCAL_SRC_FILES := test.jar LOCAL_MODULE_CLASS := JAVA_LIBRARIES + LOCAL_STATIC_ANDROID_LIBRARIES := include $(BUILD_PREBUILT) `, expected: ` @@ -522,28 +522,68 @@ include $(call all-makefiles-under,$(LOCAL_PATH)) } `, }, -} -func reformatBlueprint(input string) string { - file, errs := bpparser.Parse("<testcase>", bytes.NewBufferString(input), bpparser.NewScope(nil)) - if len(errs) > 0 { - for _, err := range errs { - fmt.Fprintln(os.Stderr, err) - } - panic(fmt.Sprintf("%d parsing errors in testcase:\n%s", len(errs), input)) - } + { + desc: "aar", + in: ` + include $(CLEAR_VARS) + LOCAL_SRC_FILES := test.java + LOCAL_RESOURCE_DIR := res + include $(BUILD_STATIC_JAVA_LIBRARY) - res, err := bpparser.Print(file) - if err != nil { - panic(fmt.Sprintf("Error printing testcase: %q", err)) - } + include $(CLEAR_VARS) + LOCAL_SRC_FILES := test.java + LOCAL_STATIC_LIBRARIES := foo + LOCAL_STATIC_ANDROID_LIBRARIES := bar + include $(BUILD_STATIC_JAVA_LIBRARY) + + include $(CLEAR_VARS) + LOCAL_SRC_FILES := test.java + LOCAL_SHARED_LIBRARIES := foo + LOCAL_SHARED_ANDROID_LIBRARIES := bar + include $(BUILD_STATIC_JAVA_LIBRARY) - return string(res) + include $(CLEAR_VARS) + LOCAL_SRC_FILES := test.java + LOCAL_STATIC_ANDROID_LIBRARIES := + include $(BUILD_STATIC_JAVA_LIBRARY) + `, + expected: ` + android_library { + srcs: ["test.java"], + resource_dirs: ["res"], + } + + android_library { + srcs: ["test.java"], + static_libs: [ + "foo", + "bar", + ], + } + + android_library { + srcs: ["test.java"], + libs: [ + "foo", + "bar", + ], + } + + java_library_static { + srcs: ["test.java"], + static_libs: [], + } + `, + }, } func TestEndToEnd(t *testing.T) { for i, test := range testCases { - expected := reformatBlueprint(test.expected) + expected, err := bpfix.Reformat(test.expected) + if err != nil { + t.Error(err) + } got, errs := convertFile(fmt.Sprintf("<testcase %d>", i), bytes.NewBufferString(test.in)) if len(errs) > 0 { diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go index 84454907..e4d4e34e 100644 --- a/bpfix/bpfix/bpfix.go +++ b/bpfix/bpfix/bpfix.go @@ -18,17 +18,36 @@ package bpfix import ( "bytes" + "errors" "fmt" + "io" "path/filepath" "github.com/google/blueprint/parser" ) +// Reformat takes a blueprint file as a string and returns a formatted version +func Reformat(input string) (string, error) { + tree, err := parse("<string>", bytes.NewBufferString(input)) + if err != nil { + return "", err + } + + res, err := parser.Print(tree) + if err != nil { + return "", err + } + + return string(res), nil +} + // A FixRequest specifies the details of which fixes to apply to an individual file // A FixRequest doesn't specify whether to do a dry run or where to write the results; that's in cmd/bpfix.go type FixRequest struct { - simplifyKnownRedundantVariables bool - rewriteIncorrectAndroidmkPrebuilts bool + simplifyKnownRedundantVariables bool + rewriteIncorrectAndroidmkPrebuilts bool + rewriteIncorrectAndroidmkAndroidLibraries bool + mergeMatchingModuleProperties bool } func NewFixRequest() FixRequest { @@ -39,24 +58,39 @@ func (r FixRequest) AddAll() (result FixRequest) { result = r result.simplifyKnownRedundantVariables = true result.rewriteIncorrectAndroidmkPrebuilts = true + result.rewriteIncorrectAndroidmkAndroidLibraries = true + result.mergeMatchingModuleProperties = true return result } -// FixTree repeatedly applies the fixes listed in the given FixRequest to the given File +type Fixer struct { + tree *parser.File +} + +func NewFixer(tree *parser.File) *Fixer { + fixer := &Fixer{tree} + + // make a copy of the tree + fixer.reparse() + + return fixer +} + +// Fix repeatedly applies the fixes listed in the given FixRequest to the given File // until there is no fix that affects the tree -func FixTree(tree *parser.File, config FixRequest) error { - prevIdentifier, err := fingerprint(tree) +func (f *Fixer) Fix(config FixRequest) (*parser.File, error) { + prevIdentifier, err := f.fingerprint() if err != nil { - return err + return nil, err } maxNumIterations := 20 i := 0 for { - err = fixTreeOnce(tree, config) - newIdentifier, err := fingerprint(tree) + err = f.fixTreeOnce(config) + newIdentifier, err := f.fingerprint() if err != nil { - return err + return nil, err } if bytes.Equal(newIdentifier, prevIdentifier) { break @@ -67,31 +101,70 @@ func FixTree(tree *parser.File, config FixRequest) error { // detect infinite loop i++ if i >= maxNumIterations { - return fmt.Errorf("Applied fixes %s times and yet the tree continued to change. Is there an infinite loop?", i) + return nil, fmt.Errorf("Applied fixes %d times and yet the tree continued to change. Is there an infinite loop?", i) break } } - return err + return f.tree, err } // returns a unique identifier for the given tree that can be used to determine whether the tree changed -func fingerprint(tree *parser.File) (fingerprint []byte, err error) { - bytes, err := parser.Print(tree) +func (f *Fixer) fingerprint() (fingerprint []byte, err error) { + bytes, err := parser.Print(f.tree) if err != nil { return nil, err } return bytes, nil } -func fixTreeOnce(tree *parser.File, config FixRequest) error { +func (f *Fixer) reparse() ([]byte, error) { + buf, err := parser.Print(f.tree) + if err != nil { + return nil, err + } + newTree, err := parse(f.tree.Name, bytes.NewReader(buf)) + if err != nil { + return nil, err + } + f.tree = newTree + return buf, nil +} + +func parse(name string, r io.Reader) (*parser.File, error) { + tree, errs := parser.Parse(name, r, parser.NewScope(nil)) + if errs != nil { + s := "parse error: " + for _, err := range errs { + s += "\n" + err.Error() + } + return nil, errors.New(s) + } + return tree, nil +} + +func (f *Fixer) fixTreeOnce(config FixRequest) error { if config.simplifyKnownRedundantVariables { - err := simplifyKnownPropertiesDuplicatingEachOther(tree) + err := f.simplifyKnownPropertiesDuplicatingEachOther() if err != nil { return err } } if config.rewriteIncorrectAndroidmkPrebuilts { - err := rewriteIncorrectAndroidmkPrebuilts(tree) + err := f.rewriteIncorrectAndroidmkPrebuilts() + if err != nil { + return err + } + } + + if config.rewriteIncorrectAndroidmkAndroidLibraries { + err := f.rewriteIncorrectAndroidmkAndroidLibraries() + if err != nil { + return err + } + } + + if config.mergeMatchingModuleProperties { + err := f.mergeMatchingModuleProperties() if err != nil { return err } @@ -99,13 +172,13 @@ func fixTreeOnce(tree *parser.File, config FixRequest) error { return nil } -func simplifyKnownPropertiesDuplicatingEachOther(tree *parser.File) error { +func (f *Fixer) simplifyKnownPropertiesDuplicatingEachOther() error { // remove from local_include_dirs anything in export_include_dirs - return removeMatchingModuleListProperties(tree, "export_include_dirs", "local_include_dirs") + return f.removeMatchingModuleListProperties("export_include_dirs", "local_include_dirs") } -func rewriteIncorrectAndroidmkPrebuilts(tree *parser.File) error { - for _, def := range tree.Defs { +func (f *Fixer) rewriteIncorrectAndroidmkPrebuilts() error { + for _, def := range f.tree.Defs { mod, ok := def.(*parser.Module) if !ok { continue @@ -127,6 +200,7 @@ func rewriteIncorrectAndroidmkPrebuilts(tree *parser.File) error { switch filepath.Ext(src.Value) { case ".jar": renameProperty(mod, "srcs", "jars") + case ".aar": renameProperty(mod, "srcs", "aars") mod.Type = "android_library_import" @@ -139,6 +213,146 @@ func rewriteIncorrectAndroidmkPrebuilts(tree *parser.File) error { return nil } +func (f *Fixer) rewriteIncorrectAndroidmkAndroidLibraries() error { + for _, def := range f.tree.Defs { + mod, ok := def.(*parser.Module) + if !ok { + continue + } + + hasAndroidLibraries := hasNonEmptyLiteralListProperty(mod, "android_libs") + hasStaticAndroidLibraries := hasNonEmptyLiteralListProperty(mod, "android_static_libs") + hasResourceDirs := hasNonEmptyLiteralListProperty(mod, "resource_dirs") + + if hasAndroidLibraries || hasStaticAndroidLibraries || hasResourceDirs { + if mod.Type == "java_library_static" { + mod.Type = "android_library" + } + } + + if mod.Type == "java_import" && !hasStaticAndroidLibraries { + removeProperty(mod, "android_static_libs") + } + + // These may conflict with existing libs and static_libs properties, but the + // mergeMatchingModuleProperties pass will fix it. + renameProperty(mod, "shared_libs", "libs") + renameProperty(mod, "android_libs", "libs") + renameProperty(mod, "android_static_libs", "static_libs") + } + + return nil +} + +func (f *Fixer) mergeMatchingModuleProperties() error { + // Make sure all the offsets are accurate + buf, err := f.reparse() + if err != nil { + return err + } + + var patchlist parser.PatchList + for _, def := range f.tree.Defs { + mod, ok := def.(*parser.Module) + if !ok { + continue + } + + err := mergeMatchingProperties(&mod.Properties, buf, &patchlist) + if err != nil { + return err + } + } + + newBuf := new(bytes.Buffer) + err = patchlist.Apply(bytes.NewReader(buf), newBuf) + if err != nil { + return err + } + + newTree, err := parse(f.tree.Name, newBuf) + if err != nil { + return err + } + + f.tree = newTree + + return nil +} + +func mergeMatchingProperties(properties *[]*parser.Property, buf []byte, patchlist *parser.PatchList) error { + seen := make(map[string]*parser.Property) + for i := 0; i < len(*properties); i++ { + property := (*properties)[i] + if prev, exists := seen[property.Name]; exists { + err := mergeProperties(prev, property, buf, patchlist) + if err != nil { + return err + } + *properties = append((*properties)[:i], (*properties)[i+1:]...) + } else { + seen[property.Name] = property + if mapProperty, ok := property.Value.(*parser.Map); ok { + err := mergeMatchingProperties(&mapProperty.Properties, buf, patchlist) + if err != nil { + return err + } + } + } + } + return nil +} + +func mergeProperties(a, b *parser.Property, buf []byte, patchlist *parser.PatchList) error { + if a.Value.Type() != b.Value.Type() { + return fmt.Errorf("type mismatch when merging properties %q: %s and %s", a.Name, a.Value.Type(), b.Value.Type()) + } + + switch a.Value.Type() { + case parser.StringType: + return fmt.Errorf("conflicting definitions of string property %q", a.Name) + case parser.ListType: + return mergeListProperties(a, b, buf, patchlist) + } + + return nil +} + +func mergeListProperties(a, b *parser.Property, buf []byte, patchlist *parser.PatchList) error { + aval, oka := a.Value.(*parser.List) + bval, okb := b.Value.(*parser.List) + if !oka || !okb { + // Merging expressions not supported yet + return nil + } + + s := string(buf[bval.LBracePos.Offset+1 : bval.RBracePos.Offset]) + if bval.LBracePos.Line != bval.RBracePos.Line { + if s[0] != '\n' { + panic("expected \n") + } + // If B is a multi line list, skip the first "\n" in case A already has a trailing "\n" + s = s[1:] + } + if aval.LBracePos.Line == aval.RBracePos.Line { + // A is a single line list with no trailing comma + if len(aval.Values) > 0 { + s = "," + s + } + } + + err := patchlist.Add(aval.RBracePos.Offset, aval.RBracePos.Offset, s) + if err != nil { + return err + } + err = patchlist.Add(b.NamePos.Offset, b.End().Offset+2, "") + if err != nil { + return err + } + + return nil +} + // removes from <items> every item present in <removals> func filterExpressionList(items *parser.List, removals *parser.List) { writeIndex := 0 @@ -163,8 +377,8 @@ func filterExpressionList(items *parser.List, removals *parser.List) { } // Remove each modules[i].Properties[<legacyName>][j] that matches a modules[i].Properties[<canonicalName>][k] -func removeMatchingModuleListProperties(tree *parser.File, canonicalName string, legacyName string) error { - for _, def := range tree.Defs { +func (f *Fixer) removeMatchingModuleListProperties(canonicalName string, legacyName string) error { + for _, def := range f.tree.Defs { mod, ok := def.(*parser.Module) if !ok { continue @@ -182,6 +396,11 @@ func removeMatchingModuleListProperties(tree *parser.File, canonicalName string, return nil } +func hasNonEmptyLiteralListProperty(mod *parser.Module, name string) bool { + list, found := getLiteralListProperty(mod, name) + return found && len(list.Values) > 0 +} + func getLiteralListProperty(mod *parser.Module, name string) (list *parser.List, found bool) { prop, ok := mod.GetProperty(name) if !ok { diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go index e17e815a..51708eb6 100644 --- a/bpfix/bpfix/bpfix_test.go +++ b/bpfix/bpfix/bpfix_test.go @@ -17,6 +17,7 @@ package bpfix import ( + "bytes" "fmt" "strings" "testing" @@ -62,14 +63,16 @@ func implFilterListTest(t *testing.T, local_include_dirs []string, export_includ t.Fatalf("%d parse errors", len(errs)) } + fixer := NewFixer(tree) + // apply simplifications - err := simplifyKnownPropertiesDuplicatingEachOther(tree) + err := fixer.simplifyKnownPropertiesDuplicatingEachOther() if len(errs) > 0 { t.Fatal(err) } // lookup legacy property - mod := tree.Defs[0].(*parser.Module) + mod := fixer.tree.Defs[0].(*parser.Module) _, found := mod.GetProperty("local_include_dirs") if !found { t.Fatalf("failed to include key local_include_dirs in parse tree") @@ -113,3 +116,130 @@ func TestSimplifyKnownVariablesDuplicatingEachOther(t *testing.T) { implFilterListTest(t, []string{}, []string{"include"}, []string{}) implFilterListTest(t, []string{}, []string{}, []string{}) } + +func TestMergeMatchingProperties(t *testing.T) { + tests := []struct { + name string + in string + out string + }{ + { + name: "empty", + in: ` + java_library { + name: "foo", + static_libs: [], + static_libs: [], + } + `, + out: ` + java_library { + name: "foo", + static_libs: [], + } + `, + }, + { + name: "single line into multiline", + in: ` + java_library { + name: "foo", + static_libs: [ + "a", + "b", + ], + //c1 + static_libs: ["c" /*c2*/], + } + `, + out: ` + java_library { + name: "foo", + static_libs: [ + "a", + "b", + "c", /*c2*/ + ], + //c1 + } + `, + }, + { + name: "multiline into multiline", + in: ` + java_library { + name: "foo", + static_libs: [ + "a", + "b", + ], + //c1 + static_libs: [ + //c2 + "c", //c3 + "d", + ], + } + `, + out: ` + java_library { + name: "foo", + static_libs: [ + "a", + "b", + //c2 + "c", //c3 + "d", + ], + //c1 + } + `, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + expected, err := Reformat(test.out) + if err != nil { + t.Error(err) + } + + in, err := Reformat(test.in) + if err != nil { + t.Error(err) + } + + tree, errs := parser.Parse("<testcase>", bytes.NewBufferString(in), parser.NewScope(nil)) + if errs != nil { + t.Fatal(errs) + } + + fixer := NewFixer(tree) + + got := "" + prev := "foo" + passes := 0 + for got != prev && passes < 10 { + err := fixer.mergeMatchingModuleProperties() + if err != nil { + t.Fatal(err) + } + + out, err := parser.Print(fixer.tree) + if err != nil { + t.Fatal(err) + } + + prev = got + got = string(out) + passes++ + } + + if got != expected { + t.Errorf("failed testcase '%s'\ninput:\n%s\n\nexpected:\n%s\ngot:\n%s\n", + test.name, in, expected, got) + } + + }) + } +} diff --git a/bpfix/cmd/bpfix.go b/bpfix/cmd/bpfix.go index 461f41dd..2fde3836 100644 --- a/bpfix/cmd/bpfix.go +++ b/bpfix/cmd/bpfix.go @@ -75,7 +75,8 @@ func processFile(filename string, in io.Reader, out io.Writer, fixRequest bpfix. } // compute and apply any requested fixes - err = bpfix.FixTree(file, fixRequest) + fixer := bpfix.NewFixer(file) + file, err = fixer.Fix(fixRequest) if err != nil { return err } diff --git a/cc/cc_test.go b/cc/cc_test.go index a4e25349..10c1aba7 100644 --- a/cc/cc_test.go +++ b/cc/cc_test.go @@ -264,7 +264,7 @@ func checkVndkModule(t *testing.T, ctx *android.TestContext, name, subDir string mod := ctx.ModuleForTests(name, vendorVariant).Module().(*Module) if !mod.hasVendorVariant() { - t.Error("%q must have vendor variant", name) + t.Errorf("%q must have vendor variant", name) } // Check library properties. diff --git a/cmd/extract_jar_packages/Android.bp b/cmd/extract_jar_packages/Android.bp new file mode 100644 index 00000000..ea0cbbf8 --- /dev/null +++ b/cmd/extract_jar_packages/Android.bp @@ -0,0 +1,25 @@ +// 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. + +blueprint_go_binary { + name: "extract_jar_packages", + deps: [ + "android-archive-zip", + "blueprint-pathtools", + ], + srcs: [ + "extract_jar_packages.go", + ], +} + diff --git a/cmd/extract_jar_packages/extract_jar_packages.go b/cmd/extract_jar_packages/extract_jar_packages.go new file mode 100644 index 00000000..fca308ff --- /dev/null +++ b/cmd/extract_jar_packages/extract_jar_packages.go @@ -0,0 +1,88 @@ +// 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 main + +import ( + "archive/zip" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "sort" + "strings" +) + +var ( + outputFile = flag.String("o", "", "output file") + prefix = flag.String("prefix", "", "prefix for each entry in the output file") + inputFile = flag.String("i", "", "input jar or srcjar") +) + +func must(err error) { + if err != nil { + log.Fatal(err) + } +} + +func fileToPackage(file string) string { + dir := filepath.Dir(file) + return strings.Replace(dir, "/", ".", -1) +} + +func main() { + flag.Usage = func() { + fmt.Fprintln(os.Stderr, "usage: extract_jar_packages -i <input file> -o <output -file> [-prefix <prefix>]") + flag.PrintDefaults() + } + + flag.Parse() + + if *outputFile == "" || *inputFile == "" { + flag.Usage() + os.Exit(1) + } + + pkgSet := make(map[string]bool) + + reader, err := zip.OpenReader(*inputFile) + if err != nil { + log.Fatal(err) + } + defer reader.Close() + + for _, f := range reader.File { + ext := filepath.Ext(f.Name) + if ext == ".java" || ext == ".class" { + pkgSet[fileToPackage(f.Name)] = true + } + } + + var pkgs []string + for k := range pkgSet { + pkgs = append(pkgs, k) + } + sort.Strings(pkgs) + + var data []byte + for _, pkg := range pkgs { + data = append(data, *prefix...) + data = append(data, pkg...) + data = append(data, "\n"...) + } + + must(ioutil.WriteFile(*outputFile, data, 0666)) +} diff --git a/cmd/extract_linker/main.go b/cmd/extract_linker/main.go index 8530b4aa..3f24ab2d 100644 --- a/cmd/extract_linker/main.go +++ b/cmd/extract_linker/main.go @@ -68,7 +68,7 @@ func main() { ef, err := elf.NewFile(f) if err != nil { - log.Fatal("Unable to read elf file: %v", err) + log.Fatalf("Unable to read elf file: %v", err) } asm := &bytes.Buffer{} @@ -123,17 +123,17 @@ func main() { if asmPath != "" { if err := ioutil.WriteFile(asmPath, asm.Bytes(), 0777); err != nil { - log.Fatal("Unable to write %q: %v", asmPath, err) + log.Fatalf("Unable to write %q: %v", asmPath, err) } } if scriptPath != "" { buf := &bytes.Buffer{} if err := linkerScriptTemplate.Execute(buf, sections); err != nil { - log.Fatal("Failed to create linker script: %v", err) + log.Fatalf("Failed to create linker script: %v", err) } if err := ioutil.WriteFile(scriptPath, buf.Bytes(), 0777); err != nil { - log.Fatal("Unable to write %q: %v", scriptPath, err) + log.Fatalf("Unable to write %q: %v", scriptPath, err) } } } diff --git a/cmd/pom2bp/Android.bp b/cmd/pom2bp/Android.bp new file mode 100644 index 00000000..0b2b7b5d --- /dev/null +++ b/cmd/pom2bp/Android.bp @@ -0,0 +1,22 @@ +// Copyright 2017 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. + +blueprint_go_binary { + name: "pom2bp", + deps: [ + "blueprint-proptools", + "bpfix-lib", + ], + srcs: ["pom2bp.go"], +} diff --git a/cmd/pom2bp/pom2bp.go b/cmd/pom2bp/pom2bp.go new file mode 100644 index 00000000..078a07dd --- /dev/null +++ b/cmd/pom2bp/pom2bp.go @@ -0,0 +1,495 @@ +// Copyright 2017 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 main + +import ( + "bufio" + "bytes" + "encoding/xml" + "flag" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "regexp" + "sort" + "strings" + "text/template" + + "github.com/google/blueprint/proptools" + + "android/soong/bpfix/bpfix" +) + +type RewriteNames []RewriteName +type RewriteName struct { + regexp *regexp.Regexp + repl string +} + +func (r *RewriteNames) String() string { + return "" +} + +func (r *RewriteNames) Set(v string) error { + split := strings.SplitN(v, "=", 2) + if len(split) != 2 { + return fmt.Errorf("Must be in the form of <regex>=<replace>") + } + regex, err := regexp.Compile(split[0]) + if err != nil { + return nil + } + *r = append(*r, RewriteName{ + regexp: regex, + repl: split[1], + }) + return nil +} + +func (r *RewriteNames) MavenToBp(groupId string, artifactId string) string { + for _, r := range *r { + if r.regexp.MatchString(groupId + ":" + artifactId) { + return r.regexp.ReplaceAllString(groupId+":"+artifactId, r.repl) + } else if r.regexp.MatchString(artifactId) { + return r.regexp.ReplaceAllString(artifactId, r.repl) + } + } + return artifactId +} + +var rewriteNames = RewriteNames{} + +type ExtraDeps map[string][]string + +func (d ExtraDeps) String() string { + return "" +} + +func (d ExtraDeps) Set(v string) error { + split := strings.SplitN(v, "=", 2) + if len(split) != 2 { + return fmt.Errorf("Must be in the form of <module>=<module>[,<module>]") + } + d[split[0]] = strings.Split(split[1], ",") + return nil +} + +var extraDeps = make(ExtraDeps) + +type Exclude map[string]bool + +func (e Exclude) String() string { + return "" +} + +func (e Exclude) Set(v string) error { + e[v] = true + return nil +} + +var excludes = make(Exclude) + +var sdkVersion string +var useVersion string + +func InList(s string, list []string) bool { + for _, l := range list { + if l == s { + return true + } + } + + return false +} + +type Dependency struct { + XMLName xml.Name `xml:"dependency"` + + BpTarget string `xml:"-"` + + GroupId string `xml:"groupId"` + ArtifactId string `xml:"artifactId"` + Version string `xml:"version"` + Type string `xml:"type"` + Scope string `xml:"scope"` +} + +func (d Dependency) BpName() string { + if d.BpTarget == "" { + d.BpTarget = rewriteNames.MavenToBp(d.GroupId, d.ArtifactId) + } + return d.BpTarget +} + +type Pom struct { + XMLName xml.Name `xml:"http://maven.apache.org/POM/4.0.0 project"` + + PomFile string `xml:"-"` + ArtifactFile string `xml:"-"` + BpTarget string `xml:"-"` + + GroupId string `xml:"groupId"` + ArtifactId string `xml:"artifactId"` + Version string `xml:"version"` + Packaging string `xml:"packaging"` + + Dependencies []*Dependency `xml:"dependencies>dependency"` +} + +func (p Pom) IsAar() bool { + return p.Packaging == "aar" +} + +func (p Pom) IsJar() bool { + return p.Packaging == "jar" +} + +func (p Pom) BpName() string { + if p.BpTarget == "" { + p.BpTarget = rewriteNames.MavenToBp(p.GroupId, p.ArtifactId) + } + return p.BpTarget +} + +func (p Pom) BpJarDeps() []string { + return p.BpDeps("jar", []string{"compile", "runtime"}) +} + +func (p Pom) BpAarDeps() []string { + return p.BpDeps("aar", []string{"compile", "runtime"}) +} + +func (p Pom) BpExtraDeps() []string { + return extraDeps[p.BpName()] +} + +// BpDeps obtains dependencies filtered by type and scope. The results of this +// method are formatted as Android.bp targets, e.g. run through MavenToBp rules. +func (p Pom) BpDeps(typeExt string, scopes []string) []string { + var ret []string + for _, d := range p.Dependencies { + if d.Type != typeExt || !InList(d.Scope, scopes) { + continue + } + name := rewriteNames.MavenToBp(d.GroupId, d.ArtifactId) + ret = append(ret, name) + } + return ret +} + +func (p Pom) SdkVersion() string { + return sdkVersion +} + +func (p *Pom) FixDeps(modules map[string]*Pom) { + for _, d := range p.Dependencies { + if d.Type == "" { + if depPom, ok := modules[d.BpName()]; ok { + // We've seen the POM for this dependency, use its packaging + // as the dependency type rather than Maven spec default. + d.Type = depPom.Packaging + } else { + // Dependency type was not specified and we don't have the POM + // for this artifact, use the default from Maven spec. + d.Type = "jar" + } + } + if d.Scope == "" { + // Scope was not specified, use the default from Maven spec. + d.Scope = "compile" + } + } +} + +var bpTemplate = template.Must(template.New("bp").Parse(` +{{if .IsAar}}android_library_import{{else}}java_import{{end}} { + name: "{{.BpName}}-nodeps", + {{if .IsAar}}aars{{else}}jars{{end}}: ["{{.ArtifactFile}}"], + sdk_version: "{{.SdkVersion}}",{{if .IsAar}} + static_libs: [{{range .BpAarDeps}} + "{{.}}",{{end}}{{range .BpExtraDeps}} + "{{.}}",{{end}} + ],{{end}} +} + +{{if .IsAar}}android_library{{else}}java_library_static{{end}} { + name: "{{.BpName}}", + sdk_version: "{{.SdkVersion}}",{{if .IsAar}} + manifest: "manifests/{{.BpName}}/AndroidManifest.xml",{{end}} + static_libs: [ + "{{.BpName}}-nodeps",{{range .BpJarDeps}} + "{{.}}",{{end}}{{range .BpAarDeps}} + "{{.}}",{{end}}{{range .BpExtraDeps}} + "{{.}}",{{end}} + ], + java_version: "1.7", +} +`)) + +func parse(filename string) (*Pom, error) { + data, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + + var pom Pom + err = xml.Unmarshal(data, &pom) + if err != nil { + return nil, err + } + + if useVersion != "" && pom.Version != useVersion { + return nil, nil + } + + if pom.Packaging == "" { + pom.Packaging = "jar" + } + + pom.PomFile = filename + pom.ArtifactFile = strings.TrimSuffix(filename, ".pom") + "." + pom.Packaging + + return &pom, nil +} + +func rerunForRegen(filename string) error { + buf, err := ioutil.ReadFile(filename) + if err != nil { + return err + } + + scanner := bufio.NewScanner(bytes.NewBuffer(buf)) + + // Skip the first line in the file + for i := 0; i < 2; i++ { + if !scanner.Scan() { + if scanner.Err() != nil { + return scanner.Err() + } else { + return fmt.Errorf("unexpected EOF") + } + } + } + + // Extract the old args from the file + line := scanner.Text() + if strings.HasPrefix(line, "// pom2bp ") { + line = strings.TrimPrefix(line, "// pom2bp ") + } else if strings.HasPrefix(line, "// pom2mk ") { + line = strings.TrimPrefix(line, "// pom2mk ") + } else if strings.HasPrefix(line, "# pom2mk ") { + line = strings.TrimPrefix(line, "# pom2mk ") + } else { + return fmt.Errorf("unexpected second line: %q", line) + } + args := strings.Split(line, " ") + lastArg := args[len(args)-1] + args = args[:len(args)-1] + + // Append all current command line args except -regen <file> to the ones from the file + for i := 1; i < len(os.Args); i++ { + if os.Args[i] == "-regen" { + i++ + } else { + args = append(args, os.Args[i]) + } + } + args = append(args, lastArg) + + cmd := os.Args[0] + " " + strings.Join(args, " ") + // Re-exec pom2bp with the new arguments + output, err := exec.Command("/bin/sh", "-c", cmd).Output() + if exitErr, _ := err.(*exec.ExitError); exitErr != nil { + return fmt.Errorf("failed to run %s\n%s", cmd, string(exitErr.Stderr)) + } else if err != nil { + return err + } + + // If the old file was a .mk file, replace it with a .bp file + if filepath.Ext(filename) == ".mk" { + os.Remove(filename) + filename = strings.TrimSuffix(filename, ".mk") + ".bp" + } + + return ioutil.WriteFile(filename, output, 0666) +} + +func main() { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, `pom2bp, a tool to create Android.bp files from maven repos + +The tool will extract the necessary information from *.pom files to create an Android.bp whose +aar libraries can be linked against when using AAPT2. + +Usage: %s [--rewrite <regex>=<replace>] [-exclude <module>] [--extra-deps <module>=<module>[,<module>]] [<dir>] [-regen <file>] + + -rewrite <regex>=<replace> + rewrite can be used to specify mappings between Maven projects and Android.bp modules. The -rewrite + option can be specified multiple times. When determining the Android.bp module for a given Maven + project, mappings are searched in the order they were specified. The first <regex> matching + either the Maven project's <groupId>:<artifactId> or <artifactId> will be used to generate + the Android.bp module name using <replace>. If no matches are found, <artifactId> is used. + -exclude <module> + Don't put the specified module in the Android.bp file. + -extra-deps <module>=<module>[,<module>] + Some Android.bp modules have transitive dependencies that must be specified when they are + depended upon (like android-support-v7-mediarouter requires android-support-v7-appcompat). + This may be specified multiple times to declare these dependencies. + -sdk-version <version> + Sets LOCAL_SDK_VERSION := <version> for all modules. + -use-version <version> + If the maven directory contains multiple versions of artifacts and their pom files, + -use-version can be used to only write Android.bp files for a specific version of those artifacts. + <dir> + The directory to search for *.pom files under. + The contents are written to stdout, to be put in the current directory (often as Android.bp) + -regen <file> + Read arguments from <file> and overwrite it (if it ends with .bp) or move it to .bp (if it + ends with .mk). + +`, os.Args[0]) + } + + var regen string + + flag.Var(&excludes, "exclude", "Exclude module") + flag.Var(&extraDeps, "extra-deps", "Extra dependencies needed when depending on a module") + flag.Var(&rewriteNames, "rewrite", "Regex(es) to rewrite artifact names") + flag.StringVar(&sdkVersion, "sdk-version", "", "What to write to LOCAL_SDK_VERSION") + flag.StringVar(&useVersion, "use-version", "", "Only read artifacts of a specific version") + flag.Bool("static-deps", false, "Ignored") + flag.StringVar(®en, "regen", "", "Rewrite specified file") + flag.Parse() + + if regen != "" { + err := rerunForRegen(regen) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + os.Exit(0) + } + + if flag.NArg() == 0 { + fmt.Fprintln(os.Stderr, "Directory argument is required") + os.Exit(1) + } else if flag.NArg() > 1 { + fmt.Fprintln(os.Stderr, "Multiple directories provided:", strings.Join(flag.Args(), " ")) + os.Exit(1) + } + + dir := flag.Arg(0) + absDir, err := filepath.Abs(dir) + if err != nil { + fmt.Fprintln(os.Stderr, "Failed to get absolute directory:", err) + os.Exit(1) + } + + var filenames []string + err = filepath.Walk(absDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + name := info.Name() + if info.IsDir() { + if strings.HasPrefix(name, ".") { + return filepath.SkipDir + } + return nil + } + + if strings.HasPrefix(name, ".") { + return nil + } + + if strings.HasSuffix(name, ".pom") { + path, err = filepath.Rel(absDir, path) + if err != nil { + return err + } + filenames = append(filenames, filepath.Join(dir, path)) + } + return nil + }) + if err != nil { + fmt.Fprintln(os.Stderr, "Error walking files:", err) + os.Exit(1) + } + + if len(filenames) == 0 { + fmt.Fprintln(os.Stderr, "Error: no *.pom files found under", dir) + os.Exit(1) + } + + sort.Strings(filenames) + + poms := []*Pom{} + modules := make(map[string]*Pom) + duplicate := false + for _, filename := range filenames { + pom, err := parse(filename) + if err != nil { + fmt.Fprintln(os.Stderr, "Error converting", filename, err) + os.Exit(1) + } + + if pom != nil { + key := pom.BpName() + if excludes[key] { + continue + } + + if old, ok := modules[key]; ok { + fmt.Fprintln(os.Stderr, "Module", key, "defined twice:", old.PomFile, pom.PomFile) + duplicate = true + } + + poms = append(poms, pom) + modules[key] = pom + } + } + if duplicate { + os.Exit(1) + } + + for _, pom := range poms { + pom.FixDeps(modules) + } + + buf := &bytes.Buffer{} + + fmt.Fprintln(buf, "// Automatically generated with:") + fmt.Fprintln(buf, "// pom2bp", strings.Join(proptools.ShellEscape(os.Args[1:]), " ")) + + for _, pom := range poms { + var err error + err = bpTemplate.Execute(buf, pom) + if err != nil { + fmt.Fprintln(os.Stderr, "Error writing", pom.PomFile, pom.BpName(), err) + os.Exit(1) + } + } + + out, err := bpfix.Reformat(buf.String()) + if err != nil { + fmt.Fprintln(os.Stderr, "Error formatting output", err) + os.Exit(1) + } + + os.Stdout.WriteString(out) +} diff --git a/cmd/pom2mk/pom2mk.go b/cmd/pom2mk/pom2mk.go index 57416243..fc836411 100644 --- a/cmd/pom2mk/pom2mk.go +++ b/cmd/pom2mk/pom2mk.go @@ -406,7 +406,7 @@ Usage: %s [--rewrite <regex>=<replace>] [-exclude <module>] [--extra-deps <modul dir := flag.Arg(0) absDir, err := filepath.Abs(dir) if err != nil { - fmt.Println(os.Stderr, "Failed to get absolute directory:", err) + fmt.Fprintln(os.Stderr, "Failed to get absolute directory:", err) os.Exit(1) } diff --git a/cmd/zipsync/zipsync.go b/cmd/zipsync/zipsync.go index 035a1455..1b300161 100644 --- a/cmd/zipsync/zipsync.go +++ b/cmd/zipsync/zipsync.go @@ -93,11 +93,11 @@ func main() { } } if filepath.IsAbs(f.Name) { - log.Fatal("%q in %q is an absolute path", f.Name, input) + log.Fatalf("%q in %q is an absolute path", f.Name, input) } if prev, exists := seen[f.Name]; exists { - log.Fatal("%q found in both %q and %q", f.Name, prev, input) + log.Fatalf("%q found in both %q and %q", f.Name, prev, input) } seen[f.Name] = input diff --git a/java/aapt2.go b/java/aapt2.go index fd7388e1..70c75071 100644 --- a/java/aapt2.go +++ b/java/aapt2.go @@ -111,15 +111,19 @@ func aapt2CompileDirs(ctx android.ModuleContext, flata android.WritablePath, dir var aapt2LinkRule = pctx.AndroidStaticRule("aapt2Link", blueprint.RuleParams{ - Command: `${config.Aapt2Cmd} link -o $out $flags --java $genDir --proguard $proguardOptions $inFlags && ` + - `${config.SoongZipCmd} -write_if_changed -jar -o $genJar -C $genDir -D $genDir`, + Command: `${config.Aapt2Cmd} link -o $out $flags --java $genDir --proguard $proguardOptions ` + + `--output-text-symbols ${rTxt} $inFlags && ` + + `${config.SoongZipCmd} -write_if_changed -jar -o $genJar -C $genDir -D $genDir &&` + + `${config.ExtractJarPackagesCmd} -i $genJar -o $extraPackages --prefix '--extra-packages '`, + CommandDeps: []string{ "${config.Aapt2Cmd}", "${config.SoongZipCmd}", + "${config.ExtractJarPackagesCmd}", }, Restat: true, }, - "flags", "inFlags", "proguardOptions", "genDir", "genJar") + "flags", "inFlags", "proguardOptions", "genDir", "genJar", "rTxt", "extraPackages") var fileListToFileRule = pctx.AndroidStaticRule("fileListToFile", blueprint.RuleParams{ @@ -129,7 +133,7 @@ var fileListToFileRule = pctx.AndroidStaticRule("fileListToFile", }) func aapt2Link(ctx android.ModuleContext, - packageRes, genJar, proguardOptions android.WritablePath, + packageRes, genJar, proguardOptions, rTxt, extraPackages android.WritablePath, flags []string, deps android.Paths, compiledRes, compiledOverlay android.Paths) { @@ -171,13 +175,15 @@ func aapt2Link(ctx android.ModuleContext, Description: "aapt2 link", Implicits: deps, Output: packageRes, - ImplicitOutputs: android.WritablePaths{proguardOptions, genJar}, + ImplicitOutputs: android.WritablePaths{proguardOptions, genJar, rTxt, extraPackages}, Args: map[string]string{ "flags": strings.Join(flags, " "), "inFlags": strings.Join(inFlags, " "), "proguardOptions": proguardOptions.String(), "genDir": genDir.String(), "genJar": genJar.String(), + "rTxt": rTxt.String(), + "extraPackages": extraPackages.String(), }, }) } diff --git a/java/aar.go b/java/aar.go index 0df3632b..9e5cddb6 100644 --- a/java/aar.go +++ b/java/aar.go @@ -16,21 +16,332 @@ package java import ( "android/soong/android" + "strings" "github.com/google/blueprint" + "github.com/google/blueprint/proptools" ) -// -// AAR (android library) prebuilts -// +type AndroidLibraryDependency interface { + Dependency + ExportPackage() android.Path + ExportedProguardFlagFiles() android.Paths + ExportedStaticPackages() android.Paths +} + func init() { android.RegisterModuleType("android_library_import", AARImportFactory) + android.RegisterModuleType("android_library", AndroidLibraryFactory) +} + +// +// AAR (android library) +// + +type androidLibraryProperties struct { + BuildAAR bool `blueprint:"mutated"` +} + +type aaptProperties struct { + // flags passed to aapt when creating the apk + Aaptflags []string + + // list of directories relative to the Blueprints file containing assets. + // Defaults to "assets" + Asset_dirs []string + + // list of directories relative to the Blueprints file containing + // Android resources + Resource_dirs []string + + // path to AndroidManifest.xml. If unset, defaults to "AndroidManifest.xml". + Manifest *string +} + +type aapt struct { + aaptSrcJar android.Path + exportPackage android.Path + manifestPath android.Path + proguardOptionsFile android.Path + rroDirs android.Paths + rTxt android.Path + extraAaptPackagesFile android.Path + + aaptProperties aaptProperties +} + +func (a *aapt) ExportPackage() android.Path { + return a.exportPackage +} + +func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkVersion string) (flags []string, deps android.Paths, + resDirs, overlayDirs []globbedResourceDir, overlayFiles, rroDirs android.Paths, manifestPath android.Path) { + + hasVersionCode := false + hasVersionName := false + for _, f := range a.aaptProperties.Aaptflags { + if strings.HasPrefix(f, "--version-code") { + hasVersionCode = true + } else if strings.HasPrefix(f, "--version-name") { + hasVersionName = true + } + } + + var linkFlags []string + + // Flags specified in Android.bp + linkFlags = append(linkFlags, a.aaptProperties.Aaptflags...) + + linkFlags = append(linkFlags, "--no-static-lib-packages") + + // Find implicit or explicit asset and resource dirs + assetDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Asset_dirs, "assets") + resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res") + + var linkDeps android.Paths + + // Glob directories into lists of paths + for _, dir := range resourceDirs { + resDirs = append(resDirs, globbedResourceDir{ + dir: dir, + files: androidResourceGlob(ctx, dir), + }) + resOverlayDirs, resRRODirs := overlayResourceGlob(ctx, dir) + overlayDirs = append(overlayDirs, resOverlayDirs...) + rroDirs = append(rroDirs, resRRODirs...) + } + + var assetFiles android.Paths + for _, dir := range assetDirs { + assetFiles = append(assetFiles, androidResourceGlob(ctx, dir)...) + } + + // App manifest file + manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml") + manifestPath = android.PathForModuleSrc(ctx, manifestFile) + linkFlags = append(linkFlags, "--manifest "+manifestPath.String()) + linkDeps = append(linkDeps, manifestPath) + + linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirs.Strings(), "-A ")) + linkDeps = append(linkDeps, assetFiles...) + + transitiveStaticLibs, libDeps, libFlags := aaptLibs(ctx, sdkVersion) + + overlayFiles = append(overlayFiles, transitiveStaticLibs...) + linkDeps = append(linkDeps, libDeps...) + linkFlags = append(linkFlags, libFlags...) + + // SDK version flags + switch sdkVersion { + case "", "current", "system_current", "test_current": + sdkVersion = proptools.NinjaEscape([]string{ctx.Config().DefaultAppTargetSdk()})[0] + } + + linkFlags = append(linkFlags, "--min-sdk-version "+sdkVersion) + linkFlags = append(linkFlags, "--target-sdk-version "+sdkVersion) + + // Version code + if !hasVersionCode { + linkFlags = append(linkFlags, "--version-code", ctx.Config().PlatformSdkVersion()) + } + + if !hasVersionName { + var versionName string + if ctx.ModuleName() == "framework-res" { + // Some builds set AppsDefaultVersionName() to include the build number ("O-123456"). aapt2 copies the + // version name of framework-res into app manifests as compileSdkVersionCodename, which confuses things + // if it contains the build number. Use the DefaultAppTargetSdk instead. + versionName = ctx.Config().DefaultAppTargetSdk() + } else { + versionName = ctx.Config().AppsDefaultVersionName() + } + versionName = proptools.NinjaEscape([]string{versionName})[0] + linkFlags = append(linkFlags, "--version-name ", versionName) + } + + return linkFlags, linkDeps, resDirs, overlayDirs, overlayFiles, rroDirs, manifestPath +} + +func (a *aapt) deps(ctx android.BottomUpMutatorContext, sdkVersion string) { + if !ctx.Config().UnbundledBuild() { + sdkDep := decodeSdkDep(ctx, sdkVersion) + if sdkDep.frameworkResModule != "" { + ctx.AddDependency(ctx.Module(), frameworkResTag, sdkDep.frameworkResModule) + } + } } +func (a *aapt) buildActions(ctx android.ModuleContext, sdkVersion string, extraLinkFlags ...string) { + linkFlags, linkDeps, resDirs, overlayDirs, overlayFiles, rroDirs, manifestPath := a.aapt2Flags(ctx, sdkVersion) + + linkFlags = append(linkFlags, extraLinkFlags...) + + packageRes := android.PathForModuleOut(ctx, "package-res.apk") + srcJar := android.PathForModuleGen(ctx, "R.jar") + proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options") + rTxt := android.PathForModuleOut(ctx, "R.txt") + // This file isn't used by Soong, but is generated for exporting + extraPackages := android.PathForModuleOut(ctx, "extra_packages") + + var compiledRes, compiledOverlay android.Paths + for _, dir := range resDirs { + compiledRes = append(compiledRes, aapt2Compile(ctx, dir.dir, dir.files).Paths()...) + } + for _, dir := range overlayDirs { + compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files).Paths()...) + } + + compiledOverlay = append(compiledOverlay, overlayFiles...) + + aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile, rTxt, extraPackages, + linkFlags, linkDeps, compiledRes, compiledOverlay) + + a.aaptSrcJar = srcJar + a.exportPackage = packageRes + a.manifestPath = manifestPath + a.proguardOptionsFile = proguardOptionsFile + a.rroDirs = rroDirs + a.extraAaptPackagesFile = extraPackages + a.rTxt = rTxt +} + +// aaptLibs collects libraries from dependencies and sdk_version and converts them into paths +func aaptLibs(ctx android.ModuleContext, sdkVersion string) (transitiveStaticLibs, deps android.Paths, + flags []string) { + + var sharedLibs android.Paths + + sdkDep := decodeSdkDep(ctx, sdkVersion) + if sdkDep.useFiles { + sharedLibs = append(sharedLibs, sdkDep.jar) + } + + ctx.VisitDirectDeps(func(module android.Module) { + var exportPackage android.Path + aarDep, _ := module.(AndroidLibraryDependency) + if aarDep != nil { + exportPackage = aarDep.ExportPackage() + } + + switch ctx.OtherModuleDependencyTag(module) { + case libTag, frameworkResTag: + if exportPackage != nil { + sharedLibs = append(sharedLibs, exportPackage) + } + case staticLibTag: + if exportPackage != nil { + transitiveStaticLibs = append(transitiveStaticLibs, exportPackage) + transitiveStaticLibs = append(transitiveStaticLibs, aarDep.ExportedStaticPackages()...) + } + } + }) + + deps = append(deps, sharedLibs...) + deps = append(deps, transitiveStaticLibs...) + + if len(transitiveStaticLibs) > 0 { + flags = append(flags, "--auto-add-overlay") + } + + for _, sharedLib := range sharedLibs { + flags = append(flags, "-I "+sharedLib.String()) + } + + transitiveStaticLibs = android.FirstUniquePaths(transitiveStaticLibs) + + return transitiveStaticLibs, deps, flags +} + +type AndroidLibrary struct { + Library + aapt + + androidLibraryProperties androidLibraryProperties + + aarFile android.WritablePath + + exportedProguardFlagFiles android.Paths + exportedStaticPackages android.Paths +} + +func (a *AndroidLibrary) ExportedProguardFlagFiles() android.Paths { + return a.exportedProguardFlagFiles +} + +func (a *AndroidLibrary) ExportedStaticPackages() android.Paths { + return a.exportedStaticPackages +} + +var _ AndroidLibraryDependency = (*AndroidLibrary)(nil) + +func (a *AndroidLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { + a.Module.deps(ctx) + if !Bool(a.properties.No_framework_libs) && !Bool(a.properties.No_standard_libs) { + a.aapt.deps(ctx, String(a.deviceProperties.Sdk_version)) + } +} + +func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { + a.aapt.buildActions(ctx, String(a.deviceProperties.Sdk_version), "--static-lib") + + ctx.CheckbuildFile(a.proguardOptionsFile) + ctx.CheckbuildFile(a.exportPackage) + ctx.CheckbuildFile(a.aaptSrcJar) + + // apps manifests are handled by aapt, don't let Module see them + a.properties.Manifest = nil + + a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, + a.proguardOptionsFile) + + a.Module.compile(ctx, a.aaptSrcJar) + + a.aarFile = android.PathForOutput(ctx, ctx.ModuleName()+".aar") + var res android.Paths + if a.androidLibraryProperties.BuildAAR { + BuildAAR(ctx, a.aarFile, a.outputFile, a.manifestPath, a.rTxt, res) + ctx.CheckbuildFile(a.aarFile) + } + + ctx.VisitDirectDeps(func(m android.Module) { + if lib, ok := m.(AndroidLibraryDependency); ok && ctx.OtherModuleDependencyTag(m) == staticLibTag { + a.exportedProguardFlagFiles = append(a.exportedProguardFlagFiles, lib.ExportedProguardFlagFiles()...) + a.exportedStaticPackages = append(a.exportedStaticPackages, lib.ExportPackage()) + a.exportedStaticPackages = append(a.exportedStaticPackages, lib.ExportedStaticPackages()...) + } + }) + + a.exportedProguardFlagFiles = android.FirstUniquePaths(a.exportedProguardFlagFiles) + a.exportedStaticPackages = android.FirstUniquePaths(a.exportedStaticPackages) +} + +func AndroidLibraryFactory() android.Module { + module := &AndroidLibrary{} + + module.AddProperties( + &module.Module.properties, + &module.Module.deviceProperties, + &module.Module.protoProperties, + &module.aaptProperties, + &module.androidLibraryProperties) + + module.androidLibraryProperties.BuildAAR = true + + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) + return module +} + +// +// AAR (android library) prebuilts +// + type AARImportProperties struct { Aars []string Sdk_version *string + + Static_libs []string + Libs []string } type AARImport struct { @@ -39,9 +350,26 @@ type AARImport struct { properties AARImportProperties - classpathFile android.WritablePath - proguardFlags android.WritablePath - exportPackage android.WritablePath + classpathFile android.WritablePath + proguardFlags android.WritablePath + exportPackage android.WritablePath + extraAaptPackagesFile android.WritablePath + + exportedStaticPackages android.Paths +} + +var _ AndroidLibraryDependency = (*AARImport)(nil) + +func (a *AARImport) ExportPackage() android.Path { + return a.exportPackage +} + +func (a *AARImport) ExportedProguardFlagFiles() android.Paths { + return android.Paths{a.proguardFlags} +} + +func (a *AARImport) ExportedStaticPackages() android.Paths { + return a.exportedStaticPackages } func (a *AARImport) Prebuilt() *android.Prebuilt { @@ -53,13 +381,15 @@ func (a *AARImport) Name() string { } func (a *AARImport) DepsMutator(ctx android.BottomUpMutatorContext) { - // TODO: this should use decodeSdkDep once that knows about current if !ctx.Config().UnbundledBuild() { - switch String(a.properties.Sdk_version) { // TODO: Res_sdk_version? - case "current", "system_current", "test_current", "": - ctx.AddDependency(ctx.Module(), frameworkResTag, "framework-res") + sdkDep := decodeSdkDep(ctx, String(a.properties.Sdk_version)) + if sdkDep.useModule && sdkDep.frameworkResModule != "" { + ctx.AddDependency(ctx.Module(), frameworkResTag, sdkDep.frameworkResModule) } } + + ctx.AddDependency(ctx.Module(), libTag, a.properties.Libs...) + ctx.AddDependency(ctx.Module(), staticLibTag, a.properties.Static_libs...) } // Unzip an AAR into its constituent files and directories. Any files in Outputs that don't exist in the AAR will be @@ -105,6 +435,8 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.exportPackage = android.PathForModuleOut(ctx, "package-res.apk") srcJar := android.PathForModuleGen(ctx, "R.jar") proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options") + rTxt := android.PathForModuleOut(ctx, "R.txt") + a.extraAaptPackagesFile = android.PathForModuleOut(ctx, "extra_packages") var linkDeps android.Paths @@ -117,30 +449,15 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { linkFlags = append(linkFlags, "--manifest "+manifest.String()) linkDeps = append(linkDeps, manifest) - // Include dirs - ctx.VisitDirectDeps(func(module android.Module) { - var depFiles android.Paths - if javaDep, ok := module.(Dependency); ok { - // TODO: shared android libraries - if ctx.OtherModuleName(module) == "framework-res" { - depFiles = android.Paths{javaDep.(*AndroidApp).exportPackage} - } - } + transitiveStaticLibs, libDeps, libFlags := aaptLibs(ctx, String(a.properties.Sdk_version)) - for _, dep := range depFiles { - linkFlags = append(linkFlags, "-I "+dep.String()) - } - linkDeps = append(linkDeps, depFiles...) - }) + linkDeps = append(linkDeps, libDeps...) + linkFlags = append(linkFlags, libFlags...) - sdkDep := decodeSdkDep(ctx, String(a.properties.Sdk_version)) - if sdkDep.useFiles { - linkFlags = append(linkFlags, "-I "+sdkDep.jar.String()) - linkDeps = append(linkDeps, sdkDep.jar) - } + overlayRes := append(android.Paths{flata}, transitiveStaticLibs...) - aapt2Link(ctx, a.exportPackage, srcJar, proguardOptionsFile, - linkFlags, linkDeps, nil, android.Paths{flata}) + aapt2Link(ctx, a.exportPackage, srcJar, proguardOptionsFile, rTxt, a.extraAaptPackagesFile, + linkFlags, linkDeps, nil, overlayRes) } var _ Dependency = (*AARImport)(nil) diff --git a/java/android_resources.go b/java/android_resources.go new file mode 100644 index 00000000..47535d29 --- /dev/null +++ b/java/android_resources.go @@ -0,0 +1,127 @@ +// 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 ( + "path/filepath" + "strings" + + "android/soong/android" +) + +func init() { + android.RegisterPreSingletonType("overlay", OverlaySingletonFactory) +} + +var androidResourceIgnoreFilenames = []string{ + ".svn", + ".git", + ".ds_store", + "*.scc", + ".*", + "CVS", + "thumbs.db", + "picasa.ini", + "*~", +} + +func androidResourceGlob(ctx android.ModuleContext, dir android.Path) android.Paths { + return ctx.GlobFiles(filepath.Join(dir.String(), "**/*"), androidResourceIgnoreFilenames) +} + +type overlayGlobResult struct { + dir string + paths android.DirectorySortedPaths + + // Set to true of the product has selected that values in this overlay should not be moved to + // Runtime Resource Overlay (RRO) packages. + excludeFromRRO bool +} + +const overlayDataKey = "overlayDataKey" + +type globbedResourceDir struct { + dir android.Path + files android.Paths +} + +func overlayResourceGlob(ctx android.ModuleContext, dir android.Path) (res []globbedResourceDir, + rroDirs android.Paths) { + + overlayData := ctx.Config().Get(overlayDataKey).([]overlayGlobResult) + + // Runtime resource overlays (RRO) may be turned on by the product config for some modules + rroEnabled := ctx.Config().EnforceRROForModule(ctx.ModuleName()) + + for _, data := range overlayData { + files := data.paths.PathsInDirectory(filepath.Join(data.dir, dir.String())) + if len(files) > 0 { + overlayModuleDir := android.PathForSource(ctx, data.dir, dir.String()) + // If enforce RRO is enabled for this module and this overlay is not in the + // exclusion list, ignore the overlay. The list of ignored overlays will be + // passed to Make to be turned into an RRO package. + if rroEnabled && !data.excludeFromRRO { + rroDirs = append(rroDirs, overlayModuleDir) + } else { + res = append(res, globbedResourceDir{ + dir: overlayModuleDir, + files: files, + }) + } + } + } + + return res, rroDirs +} + +func OverlaySingletonFactory() android.Singleton { + return overlaySingleton{} +} + +type overlaySingleton struct{} + +func (overlaySingleton) GenerateBuildActions(ctx android.SingletonContext) { + var overlayData []overlayGlobResult + overlayDirs := ctx.Config().ResourceOverlays() + for i := range overlayDirs { + // Iterate backwards through the list of overlay directories so that the later, lower-priority + // directories in the list show up earlier in the command line to aapt2. + overlay := overlayDirs[len(overlayDirs)-1-i] + var result overlayGlobResult + result.dir = overlay + + // Mark overlays that will not have Runtime Resource Overlays enforced on them + // based on the product config + result.excludeFromRRO = ctx.Config().EnforceRROExcludedOverlay(overlay) + + files, err := ctx.GlobWithDeps(filepath.Join(overlay, "**/*"), androidResourceIgnoreFilenames) + if err != nil { + ctx.Errorf("failed to glob resource dir %q: %s", overlay, err.Error()) + continue + } + var paths android.Paths + for _, f := range files { + if !strings.HasSuffix(f, "/") { + paths = append(paths, android.PathForSource(ctx, f)) + } + } + result.paths = android.PathsToDirectorySortedPaths(paths) + overlayData = append(overlayData, result) + } + + ctx.Config().Once(overlayDataKey, func() interface{} { + return overlayData + }) +} diff --git a/java/androidmk.go b/java/androidmk.go index 13966ed7..b85ecb40 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -124,6 +124,7 @@ func (prebuilt *AARImport) AndroidMk() android.AndroidMkData { fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", prebuilt.classpathFile.String()) fmt.Fprintln(w, "LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE :=", prebuilt.exportPackage.String()) fmt.Fprintln(w, "LOCAL_SOONG_EXPORT_PROGUARD_FLAGS :=", prebuilt.proguardFlags.String()) + fmt.Fprintln(w, "LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES :=", prebuilt.extraAaptPackagesFile.String()) fmt.Fprintln(w, "LOCAL_SDK_VERSION :=", String(prebuilt.properties.Sdk_version)) }, }, @@ -214,7 +215,33 @@ func (app *AndroidApp) AndroidMk() android.AndroidMkData { }, }, } +} + +func (a *AndroidLibrary) AndroidMk() android.AndroidMkData { + data := a.Library.AndroidMk() + + data.Extra = append(data.Extra, func(w io.Writer, outputFile android.Path) { + if a.proguardDictionary != nil { + fmt.Fprintln(w, "LOCAL_SOONG_PROGUARD_DICT :=", a.proguardDictionary.String()) + } + + if a.Name() == "framework-res" { + fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)") + // Make base_rules.mk not put framework-res in a subdirectory called + // framework_res. + fmt.Fprintln(w, "LOCAL_NO_STANDARD_LIBRARIES := true") + } + + fmt.Fprintln(w, "LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE :=", a.exportPackage.String()) + fmt.Fprintln(w, "LOCAL_SOONG_STATIC_LIBRARY_EXTRA_PACKAGES :=", a.extraAaptPackagesFile.String()) + fmt.Fprintln(w, "LOCAL_FULL_MANIFEST_FILE :=", a.manifestPath.String()) + fmt.Fprintln(w, "LOCAL_SOONG_EXPORT_PROGUARD_FLAGS :=", + strings.Join(a.exportedProguardFlagFiles.Strings(), " ")) + fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true") + fmt.Fprintln(w, "LOCAL_DEX_PREOPT := false") + }) + return data } func (jd *Javadoc) AndroidMk() android.AndroidMkData { diff --git a/java/app.go b/java/app.go index 9360cc56..ae0592a6 100644 --- a/java/app.go +++ b/java/app.go @@ -17,7 +17,6 @@ package java // This file contains the module types for compiling Android apps. import ( - "path/filepath" "strings" "github.com/google/blueprint/proptools" @@ -26,11 +25,9 @@ import ( ) func init() { - android.RegisterPreSingletonType("overlay", OverlaySingletonFactory) android.RegisterModuleType("android_app", AndroidAppFactory) } -// AAR prebuilts // AndroidManifest.xml merging // package splits @@ -46,91 +43,98 @@ type appProperties struct { // use to get PRODUCT-agnostic resource data like IDs and type definitions. Export_package_resources *bool - // flags passed to aapt when creating the apk - Aaptflags []string + // Specifies that this app should be installed to the priv-app directory, + // where the system will grant it additional privileges not available to + // normal apps. + Privileged *bool // list of resource labels to generate individual resource packages Package_splits []string - // list of directories relative to the Blueprints file containing assets. - // Defaults to "assets" - Asset_dirs []string - - // list of directories relative to the Blueprints file containing - // Android resources - Resource_dirs []string - Instrumentation_for *string - - // Specifies that this app should be installed to the priv-app directory, - // where the system will grant it additional privileges not available to - // normal apps. - Privileged *bool } type AndroidApp struct { - Module + Library + aapt + + certificate certificate appProperties appProperties +} - aaptSrcJar android.Path - exportPackage android.Path - rroDirs android.Paths - manifestPath android.Path - certificate certificate +func (a *AndroidApp) ExportedProguardFlagFiles() android.Paths { + return nil } +func (a *AndroidApp) ExportedStaticPackages() android.Paths { + return nil +} + +var _ AndroidLibraryDependency = (*AndroidApp)(nil) + type certificate struct { pem, key android.Path } func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) { a.Module.deps(ctx) - if !Bool(a.properties.No_framework_libs) && !Bool(a.properties.No_standard_libs) { - switch String(a.deviceProperties.Sdk_version) { // TODO: Res_sdk_version? - case "current", "system_current", "test_current", "": - ctx.AddDependency(ctx.Module(), frameworkResTag, "framework-res") - default: - // We'll already have a dependency on an sdk prebuilt android.jar - } + a.aapt.deps(ctx, String(a.deviceProperties.Sdk_version)) } } func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) { - linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, manifestPath := a.aapt2Flags(ctx) + var linkFlags []string + if String(a.appProperties.Instrumentation_for) != "" { + linkFlags = append(linkFlags, + "--rename-instrumentation-target-package", + String(a.appProperties.Instrumentation_for)) + } else { + a.properties.Instrument = true + } - packageRes := android.PathForModuleOut(ctx, "package-res.apk") - srcJar := android.PathForModuleGen(ctx, "R.jar") - proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options") + hasProduct := false + for _, f := range a.aaptProperties.Aaptflags { + if strings.HasPrefix(f, "--product") { + hasProduct = true + } + } - var compiledRes, compiledOverlay android.Paths - for _, dir := range resDirs { - compiledRes = append(compiledRes, aapt2Compile(ctx, dir.dir, dir.files).Paths()...) + // Product characteristics + if !hasProduct && len(ctx.Config().ProductAAPTCharacteristics()) > 0 { + linkFlags = append(linkFlags, "--product", ctx.Config().ProductAAPTCharacteristics()) } - for _, dir := range overlayDirs { - compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files).Paths()...) + + // Product AAPT config + for _, aaptConfig := range ctx.Config().ProductAAPTConfig() { + linkFlags = append(linkFlags, "-c", aaptConfig) } - aapt2Link(ctx, packageRes, srcJar, proguardOptionsFile, - linkFlags, linkDeps, compiledRes, compiledOverlay) + // Product AAPT preferred config + if len(ctx.Config().ProductAAPTPreferredConfig()) > 0 { + linkFlags = append(linkFlags, "--preferred-density", ctx.Config().ProductAAPTPreferredConfig()) + } - a.exportPackage = packageRes - a.aaptSrcJar = srcJar + // TODO: LOCAL_PACKAGE_OVERRIDES + // $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \ - ctx.CheckbuildFile(proguardOptionsFile) - ctx.CheckbuildFile(a.exportPackage) - ctx.CheckbuildFile(a.aaptSrcJar) + a.aapt.buildActions(ctx, String(a.deviceProperties.Sdk_version), linkFlags...) // apps manifests are handled by aapt, don't let Module see them a.properties.Manifest = nil - if String(a.appProperties.Instrumentation_for) == "" { - a.properties.Instrument = true - } + var staticLibProguardFlagFiles android.Paths + ctx.VisitDirectDeps(func(m android.Module) { + if lib, ok := m.(AndroidLibraryDependency); ok && ctx.OtherModuleDependencyTag(m) == staticLibTag { + staticLibProguardFlagFiles = append(staticLibProguardFlagFiles, lib.ExportedProguardFlagFiles()...) + } + }) + + staticLibProguardFlagFiles = android.FirstUniquePaths(staticLibProguardFlagFiles) - a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, - proguardOptionsFile) + a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, staticLibProguardFlagFiles...) + a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, a.proguardOptionsFile) if ctx.ModuleName() != "framework-res" { a.Module.compile(ctx, a.aaptSrcJar) @@ -167,8 +171,6 @@ func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) { CreateAppPackage(ctx, packageFile, a.exportPackage, a.outputFile, certificates) a.outputFile = packageFile - a.rroDirs = rroDirs - a.manifestPath = manifestPath if ctx.ModuleName() == "framework-res" { // framework-res.apk is installed as system/framework/framework-res.apk @@ -180,161 +182,6 @@ func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) { } } -var aaptIgnoreFilenames = []string{ - ".svn", - ".git", - ".ds_store", - "*.scc", - ".*", - "CVS", - "thumbs.db", - "picasa.ini", - "*~", -} - -type globbedResourceDir struct { - dir android.Path - files android.Paths -} - -func (a *AndroidApp) aapt2Flags(ctx android.ModuleContext) (flags []string, deps android.Paths, - resDirs, overlayDirs []globbedResourceDir, rroDirs android.Paths, manifestPath android.Path) { - - hasVersionCode := false - hasVersionName := false - hasProduct := false - for _, f := range a.appProperties.Aaptflags { - if strings.HasPrefix(f, "--version-code") { - hasVersionCode = true - } else if strings.HasPrefix(f, "--version-name") { - hasVersionName = true - } else if strings.HasPrefix(f, "--product") { - hasProduct = true - } - } - - var linkFlags []string - - // Flags specified in Android.bp - linkFlags = append(linkFlags, a.appProperties.Aaptflags...) - - linkFlags = append(linkFlags, "--no-static-lib-packages") - - // Find implicit or explicit asset and resource dirs - assetDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.appProperties.Asset_dirs, "assets") - resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.appProperties.Resource_dirs, "res") - - var linkDeps android.Paths - - // Glob directories into lists of paths - for _, dir := range resourceDirs { - resDirs = append(resDirs, globbedResourceDir{ - dir: dir, - files: resourceGlob(ctx, dir), - }) - resOverlayDirs, resRRODirs := overlayResourceGlob(ctx, dir) - overlayDirs = append(overlayDirs, resOverlayDirs...) - rroDirs = append(rroDirs, resRRODirs...) - } - - var assetFiles android.Paths - for _, dir := range assetDirs { - assetFiles = append(assetFiles, resourceGlob(ctx, dir)...) - } - - // App manifest file - var manifestFile string - if a.properties.Manifest == nil { - manifestFile = "AndroidManifest.xml" - } else { - manifestFile = *a.properties.Manifest - } - - manifestPath = android.PathForModuleSrc(ctx, manifestFile) - linkFlags = append(linkFlags, "--manifest "+manifestPath.String()) - linkDeps = append(linkDeps, manifestPath) - - linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirs.Strings(), "-A ")) - linkDeps = append(linkDeps, assetFiles...) - - // Include dirs - ctx.VisitDirectDeps(func(module android.Module) { - var depFiles android.Paths - if javaDep, ok := module.(Dependency); ok { - // TODO: shared android libraries - if ctx.OtherModuleName(module) == "framework-res" { - depFiles = android.Paths{javaDep.(*AndroidApp).exportPackage} - } - } - - for _, dep := range depFiles { - linkFlags = append(linkFlags, "-I "+dep.String()) - } - linkDeps = append(linkDeps, depFiles...) - }) - - sdkDep := decodeSdkDep(ctx, String(a.deviceProperties.Sdk_version)) - if sdkDep.useFiles { - linkFlags = append(linkFlags, "-I "+sdkDep.jar.String()) - linkDeps = append(linkDeps, sdkDep.jar) - } - - // SDK version flags - sdkVersion := String(a.deviceProperties.Sdk_version) - switch sdkVersion { - case "", "current", "system_current", "test_current": - sdkVersion = proptools.NinjaEscape([]string{ctx.Config().DefaultAppTargetSdk()})[0] - } - - linkFlags = append(linkFlags, "--min-sdk-version "+sdkVersion) - linkFlags = append(linkFlags, "--target-sdk-version "+sdkVersion) - - // Product characteristics - if !hasProduct && len(ctx.Config().ProductAAPTCharacteristics()) > 0 { - linkFlags = append(linkFlags, "--product", ctx.Config().ProductAAPTCharacteristics()) - } - - // Product AAPT config - for _, aaptConfig := range ctx.Config().ProductAAPTConfig() { - linkFlags = append(linkFlags, "-c", aaptConfig) - } - - // Product AAPT preferred config - if len(ctx.Config().ProductAAPTPreferredConfig()) > 0 { - linkFlags = append(linkFlags, "--preferred-density", ctx.Config().ProductAAPTPreferredConfig()) - } - - // Version code - if !hasVersionCode { - linkFlags = append(linkFlags, "--version-code", ctx.Config().PlatformSdkVersion()) - } - - if !hasVersionName { - var versionName string - if ctx.ModuleName() == "framework-res" { - // Some builds set AppsDefaultVersionName() to include the build number ("O-123456"). aapt2 copies the - // version name of framework-res into app manifests as compileSdkVersionCodename, which confuses things - // if it contains the build number. Use the DefaultAppTargetSdk instead. - versionName = ctx.Config().DefaultAppTargetSdk() - } else { - versionName = ctx.Config().AppsDefaultVersionName() - } - versionName = proptools.NinjaEscape([]string{versionName})[0] - linkFlags = append(linkFlags, "--version-name ", versionName) - } - - if String(a.appProperties.Instrumentation_for) != "" { - linkFlags = append(linkFlags, - "--rename-instrumentation-target-package", - String(a.appProperties.Instrumentation_for)) - } - - // TODO: LOCAL_PACKAGE_OVERRIDES - // $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \ - - return linkFlags, linkDeps, resDirs, overlayDirs, rroDirs, manifestPath -} - func AndroidAppFactory() android.Module { module := &AndroidApp{} @@ -344,92 +191,10 @@ func AndroidAppFactory() android.Module { module.AddProperties( &module.Module.properties, &module.Module.deviceProperties, + &module.Module.protoProperties, + &module.aaptProperties, &module.appProperties) android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) return module } - -func resourceGlob(ctx android.ModuleContext, dir android.Path) android.Paths { - return ctx.GlobFiles(filepath.Join(dir.String(), "**/*"), aaptIgnoreFilenames) -} - -type overlayGlobResult struct { - dir string - paths android.DirectorySortedPaths - - // Set to true of the product has selected that values in this overlay should not be moved to - // Runtime Resource Overlay (RRO) packages. - excludeFromRRO bool -} - -const overlayDataKey = "overlayDataKey" - -func overlayResourceGlob(ctx android.ModuleContext, dir android.Path) (res []globbedResourceDir, - rroDirs android.Paths) { - - overlayData := ctx.Config().Get(overlayDataKey).([]overlayGlobResult) - - // Runtime resource overlays (RRO) may be turned on by the product config for some modules - rroEnabled := ctx.Config().EnforceRROForModule(ctx.ModuleName()) - - for _, data := range overlayData { - files := data.paths.PathsInDirectory(filepath.Join(data.dir, dir.String())) - if len(files) > 0 { - overlayModuleDir := android.PathForSource(ctx, data.dir, dir.String()) - // If enforce RRO is enabled for this module and this overlay is not in the - // exclusion list, ignore the overlay. The list of ignored overlays will be - // passed to Make to be turned into an RRO package. - if rroEnabled && !data.excludeFromRRO { - rroDirs = append(rroDirs, overlayModuleDir) - } else { - res = append(res, globbedResourceDir{ - dir: overlayModuleDir, - files: files, - }) - } - } - } - - return res, rroDirs -} - -func OverlaySingletonFactory() android.Singleton { - return overlaySingleton{} -} - -type overlaySingleton struct{} - -func (overlaySingleton) GenerateBuildActions(ctx android.SingletonContext) { - var overlayData []overlayGlobResult - overlayDirs := ctx.Config().ResourceOverlays() - for i := range overlayDirs { - // Iterate backwards through the list of overlay directories so that the later, lower-priority - // directories in the list show up earlier in the command line to aapt2. - overlay := overlayDirs[len(overlayDirs)-1-i] - var result overlayGlobResult - result.dir = overlay - - // Mark overlays that will not have Runtime Resource Overlays enforced on them - // based on the product config - result.excludeFromRRO = ctx.Config().EnforceRROExcludedOverlay(overlay) - - files, err := ctx.GlobWithDeps(filepath.Join(overlay, "**/*"), aaptIgnoreFilenames) - if err != nil { - ctx.Errorf("failed to glob resource dir %q: %s", overlay, err.Error()) - continue - } - var paths android.Paths - for _, f := range files { - if !strings.HasSuffix(f, "/") { - paths = append(paths, android.PathForSource(ctx, f)) - } - } - result.paths = android.PathsToDirectorySortedPaths(paths) - overlayData = append(overlayData, result) - } - - ctx.Config().Once(overlayDataKey, func() interface{} { - return overlayData - }) -} diff --git a/java/app_builder.go b/java/app_builder.go index 945d7bdb..954ca446 100644 --- a/java/app_builder.go +++ b/java/app_builder.go @@ -96,3 +96,39 @@ func CreateAppPackage(ctx android.ModuleContext, outputFile android.WritablePath }, }) } + +var buildAAR = pctx.AndroidStaticRule("buildAAR", + blueprint.RuleParams{ + Command: `rm -rf ${outDir} && mkdir -p ${outDir} && ` + + `cp ${manifest} ${outDir}/AndroidManifest.xml && ` + + `cp ${classesJar} ${outDir}/classes.jar && ` + + `cp ${rTxt} ${outDir}/R.txt && ` + + `${config.SoongZipCmd} -jar -o $out -C ${outDir} -D ${outDir} ${resArgs}`, + CommandDeps: []string{"${config.SoongZipCmd}"}, + }, + "manifest", "classesJar", "rTxt", "resArgs", "outDir") + +func BuildAAR(ctx android.ModuleContext, outputFile android.WritablePath, + classesJar, manifest, rTxt android.Path, res android.Paths) { + + // TODO(ccross): uniquify and copy resources with dependencies + + deps := android.Paths{manifest, rTxt} + classesJarPath := "" + if classesJar != nil { + deps = append(deps, classesJar) + classesJarPath = classesJar.String() + } + + ctx.Build(pctx, android.BuildParams{ + Rule: buildAAR, + Implicits: deps, + Output: outputFile, + Args: map[string]string{ + "manifest": manifest.String(), + "classesJar": classesJarPath, + "rTxt": rTxt.String(), + "outDir": android.PathForModuleOut(ctx, "aar").String(), + }, + }) +} diff --git a/java/app_test.go b/java/app_test.go index 2e531303..6770119e 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -61,41 +61,44 @@ func testApp(t *testing.T, bp string) *android.TestContext { } func TestApp(t *testing.T) { - ctx := testApp(t, ` - android_app { - name: "foo", - srcs: ["a.java"], - } - `) + for _, moduleType := range []string{"android_app", "android_library"} { + t.Run(moduleType, func(t *testing.T) { + ctx := testApp(t, moduleType+` { + name: "foo", + srcs: ["a.java"], + } + `) - foo := ctx.ModuleForTests("foo", "android_common") + foo := ctx.ModuleForTests("foo", "android_common") - expectedLinkImplicits := []string{"AndroidManifest.xml"} + expectedLinkImplicits := []string{"AndroidManifest.xml"} - frameworkRes := ctx.ModuleForTests("framework-res", "android_common") - expectedLinkImplicits = append(expectedLinkImplicits, - frameworkRes.Output("package-res.apk").Output.String()) + frameworkRes := ctx.ModuleForTests("framework-res", "android_common") + expectedLinkImplicits = append(expectedLinkImplicits, + frameworkRes.Output("package-res.apk").Output.String()) - // Test the mapping from input files to compiled output file names - compile := foo.Output(compiledResourceFiles[0]) - if !reflect.DeepEqual(resourceFiles, compile.Inputs.Strings()) { - t.Errorf("expected aapt2 compile inputs expected:\n %#v\n got:\n %#v", - resourceFiles, compile.Inputs.Strings()) - } + // Test the mapping from input files to compiled output file names + compile := foo.Output(compiledResourceFiles[0]) + if !reflect.DeepEqual(resourceFiles, compile.Inputs.Strings()) { + t.Errorf("expected aapt2 compile inputs expected:\n %#v\n got:\n %#v", + resourceFiles, compile.Inputs.Strings()) + } - compiledResourceOutputs := compile.Outputs.Strings() - sort.Strings(compiledResourceOutputs) + compiledResourceOutputs := compile.Outputs.Strings() + sort.Strings(compiledResourceOutputs) - expectedLinkImplicits = append(expectedLinkImplicits, compiledResourceOutputs...) + expectedLinkImplicits = append(expectedLinkImplicits, compiledResourceOutputs...) - list := foo.Output("aapt2/res.list") - expectedLinkImplicits = append(expectedLinkImplicits, list.Output.String()) + list := foo.Output("aapt2/res.list") + expectedLinkImplicits = append(expectedLinkImplicits, list.Output.String()) - // Check that the link rule uses - res := ctx.ModuleForTests("foo", "android_common").Output("package-res.apk") - if !reflect.DeepEqual(expectedLinkImplicits, res.Implicits.Strings()) { - t.Errorf("expected aapt2 link implicits expected:\n %#v\n got:\n %#v", - expectedLinkImplicits, res.Implicits.Strings()) + // Check that the link rule uses + res := ctx.ModuleForTests("foo", "android_common").Output("package-res.apk") + if !reflect.DeepEqual(expectedLinkImplicits, res.Implicits.Strings()) { + t.Errorf("expected aapt2 link implicits expected:\n %#v\n got:\n %#v", + expectedLinkImplicits, res.Implicits.Strings()) + } + }) } } @@ -288,45 +291,47 @@ func TestAppSdkVersion(t *testing.T) { }, } - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - bp := fmt.Sprintf(`android_app { + for _, moduleType := range []string{"android_app", "android_library"} { + for _, test := range testCases { + t.Run(moduleType+" "+test.name, func(t *testing.T) { + bp := fmt.Sprintf(`%s { name: "foo", srcs: ["a.java"], sdk_version: "%s", - }`, test.sdkVersion) + }`, moduleType, test.sdkVersion) - config := testConfig(nil) - config.TestProductVariables.Platform_sdk_version = &test.platformSdkInt - config.TestProductVariables.Platform_sdk_codename = &test.platformSdkCodename - config.TestProductVariables.Platform_sdk_final = &test.platformSdkFinal + config := testConfig(nil) + config.TestProductVariables.Platform_sdk_version = &test.platformSdkInt + config.TestProductVariables.Platform_sdk_codename = &test.platformSdkCodename + config.TestProductVariables.Platform_sdk_final = &test.platformSdkFinal - ctx := testAppContext(config, bp, nil) + ctx := testAppContext(config, bp, nil) - run(t, ctx, config) + run(t, ctx, config) - foo := ctx.ModuleForTests("foo", "android_common") - link := foo.Output("package-res.apk") - linkFlags := strings.Split(link.Args["flags"], " ") - min := android.IndexList("--min-sdk-version", linkFlags) - target := android.IndexList("--target-sdk-version", linkFlags) + foo := ctx.ModuleForTests("foo", "android_common") + link := foo.Output("package-res.apk") + linkFlags := strings.Split(link.Args["flags"], " ") + min := android.IndexList("--min-sdk-version", linkFlags) + target := android.IndexList("--target-sdk-version", linkFlags) - if min == -1 || target == -1 || min == len(linkFlags)-1 || target == len(linkFlags)-1 { - t.Fatalf("missing --min-sdk-version or --target-sdk-version in link flags: %q", linkFlags) - } + if min == -1 || target == -1 || min == len(linkFlags)-1 || target == len(linkFlags)-1 { + t.Fatalf("missing --min-sdk-version or --target-sdk-version in link flags: %q", linkFlags) + } - gotMinSdkVersion := linkFlags[min+1] - gotTargetSdkVersion := linkFlags[target+1] + gotMinSdkVersion := linkFlags[min+1] + gotTargetSdkVersion := linkFlags[target+1] - if gotMinSdkVersion != test.expectedMinSdkVersion { - t.Errorf("incorrect --min-sdk-version, expected %q got %q", - test.expectedMinSdkVersion, gotMinSdkVersion) - } + if gotMinSdkVersion != test.expectedMinSdkVersion { + t.Errorf("incorrect --min-sdk-version, expected %q got %q", + test.expectedMinSdkVersion, gotMinSdkVersion) + } - if gotTargetSdkVersion != test.expectedMinSdkVersion { - t.Errorf("incorrect --target-sdk-version, expected %q got %q", - test.expectedMinSdkVersion, gotTargetSdkVersion) - } - }) + if gotTargetSdkVersion != test.expectedMinSdkVersion { + t.Errorf("incorrect --target-sdk-version, expected %q got %q", + test.expectedMinSdkVersion, gotTargetSdkVersion) + } + }) + } } } diff --git a/java/config/config.go b/java/config/config.go index 5587a16d..980db3b0 100644 --- a/java/config/config.go +++ b/java/config/config.go @@ -87,6 +87,7 @@ func init() { pctx.SourcePathVariable("GenKotlinBuildFileCmd", "build/soong/scripts/gen-kotlin-build-file.sh") pctx.SourcePathVariable("JarArgsCmd", "build/soong/scripts/jar-args.sh") + pctx.HostBinToolVariable("ExtractJarPackagesCmd", "extract_jar_packages") pctx.HostBinToolVariable("SoongZipCmd", "soong_zip") pctx.HostBinToolVariable("MergeZipsCmd", "merge_zips") pctx.HostBinToolVariable("Zip2ZipCmd", "zip2zip") diff --git a/java/config/makevars.go b/java/config/makevars.go index 5210f202..27c7daaa 100644 --- a/java/config/makevars.go +++ b/java/config/makevars.go @@ -77,4 +77,6 @@ func makeVarsProvider(ctx android.MakeVarsContext) { ctx.Strict("JACOCO_CLI_JAR", "${JacocoCLIJar}") ctx.Strict("DEFAULT_JACOCO_EXCLUDE_FILTER", strings.Join(DefaultJacocoExcludeFilter, ",")) + + ctx.Strict("EXTRACT_JAR_PACKAGES", "${ExtractJarPackagesCmd}") } diff --git a/java/java.go b/java/java.go index d4db7157..619ae754 100644 --- a/java/java.go +++ b/java/java.go @@ -312,6 +312,8 @@ type sdkDep struct { module string systemModules string + frameworkResModule string + jar android.Path aidl android.Path } @@ -406,6 +408,7 @@ func decodeSdkDep(ctx android.BaseContext, v string) sdkDep { // useModule: true, // module: m, // systemModules: m + "_system_modules", + // frameworkResModule: r, // } //} @@ -416,16 +419,17 @@ func decodeSdkDep(ctx android.BaseContext, v string) sdkDep { switch v { case "": return sdkDep{ - useDefaultLibs: true, + useDefaultLibs: true, + frameworkResModule: "framework-res", } // TODO(ccross): re-enable these once we generate stubs, until then // use the stubs in prebuilts/sdk/*current //case "current": - // return toModule("android_stubs_current") + // return toModule("android_stubs_current", "framework-res") //case "system_current": - // return toModule("android_system_stubs_current") + // return toModule("android_system_stubs_current", "framework-res") //case "test_current": - // return toModule("android_test_stubs_current") + // return toModule("android_test_stubs_current", "framework-res") default: return toFile(v) } @@ -592,7 +596,10 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { tag := ctx.OtherModuleDependencyTag(module) if to, ok := module.(*Library); ok { - checkLinkType(ctx, j, to, tag.(dependencyTag)) + switch tag { + case bootClasspathTag, libTag, staticLibTag: + checkLinkType(ctx, j, to, tag.(dependencyTag)) + } } switch dep := module.(type) { case Dependency: diff --git a/java/resources.go b/java/java_resources.go index a596fd7e..a596fd7e 100644 --- a/java/resources.go +++ b/java/java_resources.go diff --git a/java/java_test.go b/java/java_test.go index 015c2331..fb8cc949 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -70,6 +70,7 @@ func testContext(config android.Config, bp string, ctx := android.NewTestArchContext() ctx.RegisterModuleType("android_app", android.ModuleFactoryAdaptor(AndroidAppFactory)) + ctx.RegisterModuleType("android_library", android.ModuleFactoryAdaptor(AndroidLibraryFactory)) ctx.RegisterModuleType("java_binary_host", android.ModuleFactoryAdaptor(BinaryHostFactory)) ctx.RegisterModuleType("java_library", android.ModuleFactoryAdaptor(LibraryFactory(true))) ctx.RegisterModuleType("java_library_host", android.ModuleFactoryAdaptor(LibraryHostFactory)) diff --git a/java/support_libraries.go b/java/support_libraries.go new file mode 100644 index 00000000..320afae1 --- /dev/null +++ b/java/support_libraries.go @@ -0,0 +1,66 @@ +// 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 ( + "sort" + "strings" + + "android/soong/android" +) + +func init() { + android.RegisterMakeVarsProvider(pctx, supportLibrariesMakeVarsProvider) +} + +func supportLibrariesMakeVarsProvider(ctx android.MakeVarsContext) { + var supportAars, supportJars []string + + sctx := ctx.SingletonContext() + sctx.VisitAllModules(func(module android.Module) { + dir := sctx.ModuleDir(module) + switch { + case strings.HasPrefix(dir, "prebuilts/sdk/current/extras"), + dir == "prebuilts/sdk/current/androidx", + dir == "prebuilts/sdk/current/car", + dir == "prebuilts/sdk/current/optional", + dir == "prebuilts/sdk/current/support": + // Support library + default: + // Not a support library + return + } + + name := sctx.ModuleName(module) + if strings.HasSuffix(name, "-nodeps") { + return + } + + switch module.(type) { + case *AndroidLibrary, *AARImport: + supportAars = append(supportAars, name) + case *Library, *Import: + supportJars = append(supportJars, name) + default: + sctx.ModuleErrorf(module, "unknown module type %t", module) + } + }) + + sort.Strings(supportAars) + sort.Strings(supportJars) + + ctx.Strict("SUPPORT_LIBRARIES_AARS", strings.Join(supportAars, " ")) + ctx.Strict("SUPPORT_LIBRARIES_JARS", strings.Join(supportJars, " ")) +} diff --git a/ui/logger/logger.go b/ui/logger/logger.go index 15c413dd..c763e50c 100644 --- a/ui/logger/logger.go +++ b/ui/logger/logger.go @@ -85,7 +85,7 @@ func fileRotation(from, baseName, ext string, cur, max int) error { } if err := os.Rename(from, newName); err != nil { - return fmt.Errorf("Failed to rotate", from, "to", newName, ".", err) + return fmt.Errorf("Failed to rotate %s to %s. %s", from, newName, err) } return nil } diff --git a/ui/logger/logger_test.go b/ui/logger/logger_test.go index dc6f2e91..bdf0231a 100644 --- a/ui/logger/logger_test.go +++ b/ui/logger/logger_test.go @@ -106,7 +106,7 @@ func TestPanic(t *testing.T) { if p == panicValue { os.Exit(42) } else { - fmt.Fprintln(os.Stderr, "Expected %q, got %v", panicValue, p) + fmt.Fprintf(os.Stderr, "Expected %q, got %v\n", panicValue, p) os.Exit(3) } }() |