From 031d2b32e35314270a6d0cec6233e0649523133c Mon Sep 17 00:00:00 2001 From: Logan Chien Date: Thu, 26 Jul 2018 00:19:50 +0800 Subject: [PATCH] Support prebuilt shared libs end with versions This commit changes how `cc/androidmk.go` generates LOCAL_MODULE_STEM, LOCAL_MODULE_SUFFIX, and LOCAL_BUILT_MODULE_STEM. Now, `splitFileExt()` takes a file name and a list of expected file extensions. `splitFileExt()` searches the first occurrence of expected file extensions in the file name. If it can not find any, it will simply return the last file extension. Before this commit, `cc/androidmk.go` simply extracts the last file extension (e.g. `.so`). However, if the prebuilt shared libs end with version numbers (e.g. `libc++.so.1`), it will use `$(LOCAL_MODULE).1` as LOCAL_BUILT_MODULE_STEM and this will lead to missing target ninja error. Bug: 111579848 Test: Build a program that links libc++_host (from prebuilts/clang) Change-Id: Id96726c69705d518ea725bb6abd8ff4527ca0cbc --- Android.bp | 1 + cc/androidmk.go | 27 ++++++++++++-------- cc/util.go | 27 ++++++++++++++++++++ cc/util_test.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 11 deletions(-) create mode 100644 cc/util_test.go 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) + } + }) +}