diff --git a/Android.bp b/Android.bp index 26aeac23d..83ec5696e 100644 --- a/Android.bp +++ b/Android.bp @@ -174,6 +174,7 @@ bootstrap_go_package { "cc/gen_test.go", "cc/library_test.go", "cc/test_data_test.go", + "cc/util_test.go", ], pluginFor: ["soong_build"], } diff --git a/cc/androidmk.go b/cc/androidmk.go index 228b64af7..c510d98dc 100644 --- a/cc/androidmk.go +++ b/cc/androidmk.go @@ -192,7 +192,9 @@ func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.An } } - fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+outputFile.Ext()) + _, _, ext := splitFileExt(outputFile.Base()) + + fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+ext) fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=") @@ -286,7 +288,8 @@ func (test *testLibrary) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkD func (library *toolchainLibraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) { ret.Class = "STATIC_LIBRARIES" ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) { - fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+outputFile.Ext()) + _, suffix, _ := splitFileExt(outputFile.Base()) + fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix) fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=") }) } @@ -327,8 +330,8 @@ func (installer *baseInstaller) AndroidMk(ctx AndroidMkContext, ret *android.And ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) { path := installer.path.RelPathString() dir, file := filepath.Split(path) - stem := strings.TrimSuffix(file, filepath.Ext(file)) - fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+filepath.Ext(file)) + stem, suffix, _ := splitFileExt(file) + fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix) fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(OUT_DIR)/"+filepath.Clean(dir)) fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem) }) @@ -340,9 +343,9 @@ func (c *stubDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkDa ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) { path, file := filepath.Split(c.installPath.String()) - stem := strings.TrimSuffix(file, filepath.Ext(file)) + stem, suffix, _ := splitFileExt(file) fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=") - fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+outputFile.Ext()) + fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix) fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path) fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem) fmt.Fprintln(w, "LOCAL_NO_NOTICE_FILE := true") @@ -360,8 +363,9 @@ func (c *llndkStubDecorator) AndroidMk(ctx AndroidMkContext, ret *android.Androi ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) { c.libraryDecorator.androidMkWriteExportedFlags(w) + _, _, ext := splitFileExt(outputFile.Base()) - fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+outputFile.Ext()) + fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+ext) fmt.Fprintln(w, "LOCAL_STRIP_MODULE := false") fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=") fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true") @@ -380,12 +384,12 @@ func (c *vndkPrebuiltLibraryDecorator) AndroidMk(ctx AndroidMkContext, ret *andr path := c.path.RelPathString() dir, file := filepath.Split(path) - stem := strings.TrimSuffix(file, filepath.Ext(file)) + stem, suffix, ext := splitFileExt(file) fmt.Fprintln(w, "LOCAL_STRIP_MODULE := false") fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=") fmt.Fprintln(w, "LOCAL_USE_VNDK := true") - fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+outputFile.Ext()) - fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+filepath.Ext(file)) + fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+ext) + fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix) fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(OUT_DIR)/"+filepath.Clean(dir)) fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem) }) @@ -408,8 +412,9 @@ func (c *vendorPublicLibraryStubDecorator) AndroidMk(ctx AndroidMkContext, ret * ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) { c.libraryDecorator.androidMkWriteExportedFlags(w) + _, _, ext := splitFileExt(outputFile.Base()) - fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+outputFile.Ext()) + fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+ext) fmt.Fprintln(w, "LOCAL_STRIP_MODULE := false") fmt.Fprintln(w, "LOCAL_SYSTEM_SHARED_LIBRARIES :=") fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true") diff --git a/cc/util.go b/cc/util.go index 1e4a0c0ca..8de421032 100644 --- a/cc/util.go +++ b/cc/util.go @@ -16,6 +16,7 @@ package cc import ( "fmt" + "path/filepath" "regexp" "strings" @@ -102,3 +103,29 @@ func addSuffix(list []string, suffix string) []string { } return list } + +var shlibVersionPattern = regexp.MustCompile("(?:\\.\\d+)+") + +// splitFileExt splits a file name into root, suffix and ext. root stands for the file name without +// the file extension and the version number (e.g. "libexample"). suffix stands for the +// concatenation of the file extension and the version number (e.g. ".so.1.0"). ext stands for the +// file extension after the version numbers are trimmed (e.g. ".so"). +func splitFileExt(name string) (string, string, string) { + // Extract and trim the shared lib version number if the file name ends with dot digits. + suffix := "" + matches := shlibVersionPattern.FindAllStringIndex(name, -1) + if len(matches) > 0 { + lastMatch := matches[len(matches)-1] + if lastMatch[1] == len(name) { + suffix = name[lastMatch[0]:lastMatch[1]] + name = name[0:lastMatch[0]] + } + } + + // Extract the file name root and the file extension. + ext := filepath.Ext(name) + root := strings.TrimSuffix(name, ext) + suffix = ext + suffix + + return root, suffix, ext +} diff --git a/cc/util_test.go b/cc/util_test.go new file mode 100644 index 000000000..3108294d0 --- /dev/null +++ b/cc/util_test.go @@ -0,0 +1,68 @@ +// 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 cc + +import ( + "testing" +) + +func TestSplitFileExt(t *testing.T) { + t.Run("soname with version", func(t *testing.T) { + root, suffix, ext := splitFileExt("libtest.so.1.0.30") + expected := "libtest" + if root != expected { + t.Errorf("root should be %q but got %q", expected, root) + } + expected = ".so.1.0.30" + if suffix != expected { + t.Errorf("suffix should be %q but got %q", expected, suffix) + } + expected = ".so" + if ext != expected { + t.Errorf("ext should be %q but got %q", expected, ext) + } + }) + + t.Run("version numbers in the middle should be ignored", func(t *testing.T) { + root, suffix, ext := splitFileExt("libtest.1.0.30.so") + expected := "libtest.1.0.30" + if root != expected { + t.Errorf("root should be %q but got %q", expected, root) + } + expected = ".so" + if suffix != expected { + t.Errorf("suffix should be %q but got %q", expected, suffix) + } + expected = ".so" + if ext != expected { + t.Errorf("ext should be %q but got %q", expected, ext) + } + }) + + t.Run("no known file extension", func(t *testing.T) { + root, suffix, ext := splitFileExt("test.exe") + expected := "test" + if root != expected { + t.Errorf("root should be %q but got %q", expected, root) + } + expected = ".exe" + if suffix != expected { + t.Errorf("suffix should be %q but got %q", expected, suffix) + } + if ext != expected { + t.Errorf("ext should be %q but got %q", expected, ext) + } + }) +}