platform_build_soong/cc/library.go
Dan Willemsen a03cf6d322 Add clang-tidy support
For every file which we can run clang-tidy (C/C++ clang-built), we add a
new build node that depends on the object file (since clang-tidy does
not export a depfile), and is depended on by the link step. This is
better than how we're doing it in make, since calling tidy can be turned
on or off without needing to rebuild the object files.

This does not attempt to port WITH_TIDY_ONLY from Make, since the way
that it works is broken (due to the lack of a depfile).

Bug: 32244182
Test: WITH_TIDY=true mmma -j bionic/libc
Test: ./soong (Setting ClangTidy: true)
Change-Id: I40bbb5bb00d292d72bf1c293b93080b5f9f6d8ea
2016-10-31 16:21:01 -07:00

585 lines
18 KiB
Go

// Copyright 2016 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 (
"strings"
"github.com/google/blueprint"
"github.com/google/blueprint/pathtools"
"android/soong/android"
)
type LibraryProperties struct {
Static struct {
Srcs []string `android:"arch_variant"`
Cflags []string `android:"arch_variant"`
Enabled *bool `android:"arch_variant"`
Whole_static_libs []string `android:"arch_variant"`
Static_libs []string `android:"arch_variant"`
Shared_libs []string `android:"arch_variant"`
} `android:"arch_variant"`
Shared struct {
Srcs []string `android:"arch_variant"`
Cflags []string `android:"arch_variant"`
Enabled *bool `android:"arch_variant"`
Whole_static_libs []string `android:"arch_variant"`
Static_libs []string `android:"arch_variant"`
Shared_libs []string `android:"arch_variant"`
} `android:"arch_variant"`
// local file name to pass to the linker as --version_script
Version_script *string `android:"arch_variant"`
// local file name to pass to the linker as -unexported_symbols_list
Unexported_symbols_list *string `android:"arch_variant"`
// local file name to pass to the linker as -force_symbols_not_weak_list
Force_symbols_not_weak_list *string `android:"arch_variant"`
// local file name to pass to the linker as -force_symbols_weak_list
Force_symbols_weak_list *string `android:"arch_variant"`
// rename host libraries to prevent overlap with system installed libraries
Unique_host_soname *bool
Proto struct {
// export headers generated from .proto sources
Export_proto_headers bool
}
VariantName string `blueprint:"mutated"`
// Build a static variant
BuildStatic bool `blueprint:"mutated"`
// Build a shared variant
BuildShared bool `blueprint:"mutated"`
// This variant is shared
VariantIsShared bool `blueprint:"mutated"`
// This variant is static
VariantIsStatic bool `blueprint:"mutated"`
}
type FlagExporterProperties struct {
// list of directories relative to the Blueprints file that will
// be added to the include path using -I for any module that links against this module
Export_include_dirs []string `android:"arch_variant"`
}
func init() {
android.RegisterModuleType("cc_library_static", libraryStaticFactory)
android.RegisterModuleType("cc_library_shared", librarySharedFactory)
android.RegisterModuleType("cc_library", libraryFactory)
android.RegisterModuleType("cc_library_host_static", libraryHostStaticFactory)
android.RegisterModuleType("cc_library_host_shared", libraryHostSharedFactory)
}
// Module factory for combined static + shared libraries, device by default but with possible host
// support
func libraryFactory() (blueprint.Module, []interface{}) {
module, _ := NewLibrary(android.HostAndDeviceSupported, true, true)
return module.Init()
}
// Module factory for static libraries
func libraryStaticFactory() (blueprint.Module, []interface{}) {
module, _ := NewLibrary(android.HostAndDeviceSupported, false, true)
return module.Init()
}
// Module factory for shared libraries
func librarySharedFactory() (blueprint.Module, []interface{}) {
module, _ := NewLibrary(android.HostAndDeviceSupported, true, false)
return module.Init()
}
// Module factory for host static libraries
func libraryHostStaticFactory() (blueprint.Module, []interface{}) {
module, _ := NewLibrary(android.HostSupported, false, true)
return module.Init()
}
// Module factory for host shared libraries
func libraryHostSharedFactory() (blueprint.Module, []interface{}) {
module, _ := NewLibrary(android.HostSupported, true, false)
return module.Init()
}
type flagExporter struct {
Properties FlagExporterProperties
flags []string
flagsDeps android.Paths
}
func (f *flagExporter) exportIncludes(ctx ModuleContext, inc string) {
includeDirs := android.PathsForModuleSrc(ctx, f.Properties.Export_include_dirs)
for _, dir := range includeDirs.Strings() {
f.flags = append(f.flags, inc+dir)
}
}
func (f *flagExporter) reexportFlags(flags []string) {
f.flags = append(f.flags, flags...)
}
func (f *flagExporter) reexportDeps(deps android.Paths) {
f.flagsDeps = append(f.flagsDeps, deps...)
}
func (f *flagExporter) exportedFlags() []string {
return f.flags
}
func (f *flagExporter) exportedFlagsDeps() android.Paths {
return f.flagsDeps
}
type exportedFlagsProducer interface {
exportedFlags() []string
exportedFlagsDeps() android.Paths
}
var _ exportedFlagsProducer = (*flagExporter)(nil)
// libraryDecorator wraps baseCompiler, baseLinker and baseInstaller to provide library-specific
// functionality: static vs. shared linkage, reusing object files for shared libraries
type libraryDecorator struct {
Properties LibraryProperties
// For reusing static library objects for shared library
reuseObjects Objects
// table-of-contents file to optimize out relinking when possible
tocFile android.OptionalPath
flagExporter
stripper
relocationPacker
// If we're used as a whole_static_lib, our missing dependencies need
// to be given
wholeStaticMissingDeps []string
// For whole_static_libs
objects Objects
// Uses the module's name if empty, but can be overridden. Does not include
// shlib suffix.
libName string
sanitize *sanitize
// Decorated interafaces
*baseCompiler
*baseLinker
*baseInstaller
}
func (library *libraryDecorator) linkerProps() []interface{} {
var props []interface{}
props = append(props, library.baseLinker.linkerProps()...)
return append(props,
&library.Properties,
&library.flagExporter.Properties,
&library.stripper.StripProperties,
&library.relocationPacker.Properties)
}
func (library *libraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
flags = library.baseLinker.linkerFlags(ctx, flags)
// MinGW spits out warnings about -fPIC even for -fpie?!) being ignored because
// all code is position independent, and then those warnings get promoted to
// errors.
if ctx.Os() != android.Windows {
flags.CFlags = append(flags.CFlags, "-fPIC")
}
if library.static() {
flags.CFlags = append(flags.CFlags, library.Properties.Static.Cflags...)
} else {
flags.CFlags = append(flags.CFlags, library.Properties.Shared.Cflags...)
}
if !library.static() {
libName := library.getLibName(ctx)
// GCC for Android assumes that -shared means -Bsymbolic, use -Wl,-shared instead
sharedFlag := "-Wl,-shared"
if flags.Clang || ctx.Host() {
sharedFlag = "-shared"
}
var f []string
if ctx.Device() {
f = append(f,
"-nostdlib",
"-Wl,--gc-sections",
)
}
if ctx.Darwin() {
f = append(f,
"-dynamiclib",
"-single_module",
"-install_name @rpath/"+libName+flags.Toolchain.ShlibSuffix(),
)
if ctx.Arch().ArchType == android.X86 {
f = append(f,
"-read_only_relocs suppress",
)
}
} else {
f = append(f,
sharedFlag,
"-Wl,-soname,"+libName+flags.Toolchain.ShlibSuffix())
}
flags.LdFlags = append(f, flags.LdFlags...)
}
return flags
}
func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
objs := library.baseCompiler.compile(ctx, flags, deps)
library.reuseObjects = objs
buildFlags := flagsToBuilderFlags(flags)
if library.static() {
srcs := android.PathsForModuleSrc(ctx, library.Properties.Static.Srcs)
objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceStaticLibrary,
srcs, library.baseCompiler.deps))
} else {
srcs := android.PathsForModuleSrc(ctx, library.Properties.Shared.Srcs)
objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceSharedLibrary,
srcs, library.baseCompiler.deps))
}
return objs
}
type libraryInterface interface {
getWholeStaticMissingDeps() []string
static() bool
objs() Objects
reuseObjs() Objects
toc() android.OptionalPath
// Returns true if the build options for the module have selected a static or shared build
buildStatic() bool
buildShared() bool
// Sets whether a specific variant is static or shared
setStatic(bool)
}
func (library *libraryDecorator) getLibName(ctx ModuleContext) string {
name := library.libName
if name == "" {
name = ctx.baseModuleName()
}
if ctx.Host() && Bool(library.Properties.Unique_host_soname) {
if !strings.HasSuffix(name, "-host") {
name = name + "-host"
}
}
return name + library.Properties.VariantName
}
func (library *libraryDecorator) linkerInit(ctx BaseModuleContext) {
location := InstallInSystem
if library.sanitize.inData() {
location = InstallInData
}
library.baseInstaller.location = location
library.baseLinker.linkerInit(ctx)
library.relocationPacker.packingInit(ctx)
}
func (library *libraryDecorator) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
deps = library.baseLinker.linkerDeps(ctx, deps)
if library.static() {
deps.WholeStaticLibs = append(deps.WholeStaticLibs,
library.Properties.Static.Whole_static_libs...)
deps.StaticLibs = append(deps.StaticLibs, library.Properties.Static.Static_libs...)
deps.SharedLibs = append(deps.SharedLibs, library.Properties.Static.Shared_libs...)
} else {
if ctx.Device() && !Bool(library.baseLinker.Properties.Nocrt) {
if !ctx.sdk() {
deps.CrtBegin = "crtbegin_so"
deps.CrtEnd = "crtend_so"
} else {
deps.CrtBegin = "ndk_crtbegin_so." + ctx.sdkVersion()
deps.CrtEnd = "ndk_crtend_so." + ctx.sdkVersion()
}
}
deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.Properties.Shared.Whole_static_libs...)
deps.StaticLibs = append(deps.StaticLibs, library.Properties.Shared.Static_libs...)
deps.SharedLibs = append(deps.SharedLibs, library.Properties.Shared.Shared_libs...)
}
return deps
}
func (library *libraryDecorator) linkStatic(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
library.objects = deps.WholeStaticLibObjs.Copy()
library.objects = library.objects.Append(objs)
outputFile := android.PathForModuleOut(ctx,
ctx.ModuleName()+library.Properties.VariantName+staticLibraryExtension)
if ctx.Darwin() {
TransformDarwinObjToStaticLib(ctx, library.objects.objFiles, flagsToBuilderFlags(flags), outputFile, objs.tidyFiles)
} else {
TransformObjToStaticLib(ctx, library.objects.objFiles, flagsToBuilderFlags(flags), outputFile, objs.tidyFiles)
}
library.wholeStaticMissingDeps = ctx.GetMissingDependencies()
ctx.CheckbuildFile(outputFile)
return outputFile
}
func (library *libraryDecorator) linkShared(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
var linkerDeps android.Paths
versionScript := android.OptionalPathForModuleSrc(ctx, library.Properties.Version_script)
unexportedSymbols := android.OptionalPathForModuleSrc(ctx, library.Properties.Unexported_symbols_list)
forceNotWeakSymbols := android.OptionalPathForModuleSrc(ctx, library.Properties.Force_symbols_not_weak_list)
forceWeakSymbols := android.OptionalPathForModuleSrc(ctx, library.Properties.Force_symbols_weak_list)
if !ctx.Darwin() {
if versionScript.Valid() {
flags.LdFlags = append(flags.LdFlags, "-Wl,--version-script,"+versionScript.String())
linkerDeps = append(linkerDeps, versionScript.Path())
}
if unexportedSymbols.Valid() {
ctx.PropertyErrorf("unexported_symbols_list", "Only supported on Darwin")
}
if forceNotWeakSymbols.Valid() {
ctx.PropertyErrorf("force_symbols_not_weak_list", "Only supported on Darwin")
}
if forceWeakSymbols.Valid() {
ctx.PropertyErrorf("force_symbols_weak_list", "Only supported on Darwin")
}
} else {
if versionScript.Valid() {
ctx.PropertyErrorf("version_script", "Not supported on Darwin")
}
if unexportedSymbols.Valid() {
flags.LdFlags = append(flags.LdFlags, "-Wl,-unexported_symbols_list,"+unexportedSymbols.String())
linkerDeps = append(linkerDeps, unexportedSymbols.Path())
}
if forceNotWeakSymbols.Valid() {
flags.LdFlags = append(flags.LdFlags, "-Wl,-force_symbols_not_weak_list,"+forceNotWeakSymbols.String())
linkerDeps = append(linkerDeps, forceNotWeakSymbols.Path())
}
if forceWeakSymbols.Valid() {
flags.LdFlags = append(flags.LdFlags, "-Wl,-force_symbols_weak_list,"+forceWeakSymbols.String())
linkerDeps = append(linkerDeps, forceWeakSymbols.Path())
}
}
fileName := library.getLibName(ctx) + flags.Toolchain.ShlibSuffix()
outputFile := android.PathForModuleOut(ctx, fileName)
ret := outputFile
builderFlags := flagsToBuilderFlags(flags)
if !ctx.Darwin() {
// Optimize out relinking against shared libraries whose interface hasn't changed by
// depending on a table of contents file instead of the library itself.
tocPath := outputFile.RelPathString()
tocPath = pathtools.ReplaceExtension(tocPath, flags.Toolchain.ShlibSuffix()[1:]+".toc")
tocFile := android.PathForOutput(ctx, tocPath)
library.tocFile = android.OptionalPathForPath(tocFile)
TransformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
}
if library.relocationPacker.needsPacking(ctx) {
packedOutputFile := outputFile
outputFile = android.PathForModuleOut(ctx, "unpacked", fileName)
library.relocationPacker.pack(ctx, outputFile, packedOutputFile, builderFlags)
}
if library.stripper.needsStrip(ctx) {
strippedOutputFile := outputFile
outputFile = android.PathForModuleOut(ctx, "unstripped", fileName)
library.stripper.strip(ctx, outputFile, strippedOutputFile, builderFlags)
}
sharedLibs := deps.SharedLibs
sharedLibs = append(sharedLibs, deps.LateSharedLibs...)
// TODO(danalbert): Clean this up when soong supports prebuilts.
if strings.HasPrefix(ctx.selectedStl(), "ndk_libc++") {
libDir := getNdkStlLibDir(ctx, flags.Toolchain, "libc++")
if strings.HasSuffix(ctx.selectedStl(), "_shared") {
deps.StaticLibs = append(deps.StaticLibs,
libDir.Join(ctx, "libandroid_support.a"))
} else {
deps.StaticLibs = append(deps.StaticLibs,
libDir.Join(ctx, "libc++abi.a"),
libDir.Join(ctx, "libandroid_support.a"))
}
if ctx.Arch().ArchType == android.Arm {
deps.StaticLibs = append(deps.StaticLibs,
libDir.Join(ctx, "libunwind.a"))
}
}
linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
linkerDeps = append(linkerDeps, objs.tidyFiles...)
TransformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile)
return ret
}
func (library *libraryDecorator) link(ctx ModuleContext,
flags Flags, deps PathDeps, objs Objects) android.Path {
objs = objs.Append(deps.Objs)
var out android.Path
if library.static() {
out = library.linkStatic(ctx, flags, deps, objs)
} else {
out = library.linkShared(ctx, flags, deps, objs)
}
library.exportIncludes(ctx, "-I")
library.reexportFlags(deps.ReexportedFlags)
library.reexportDeps(deps.ReexportedFlagsDeps)
if library.baseCompiler.hasProto() {
if library.Properties.Proto.Export_proto_headers {
library.reexportFlags([]string{
"-I" + protoSubDir(ctx).String(),
"-I" + protoDir(ctx).String(),
})
library.reexportDeps(library.baseCompiler.deps) // TODO: restrict to proto deps
}
}
return out
}
func (library *libraryDecorator) buildStatic() bool {
return library.Properties.BuildStatic &&
(library.Properties.Static.Enabled == nil || *library.Properties.Static.Enabled)
}
func (library *libraryDecorator) buildShared() bool {
return library.Properties.BuildShared &&
(library.Properties.Shared.Enabled == nil || *library.Properties.Shared.Enabled)
}
func (library *libraryDecorator) getWholeStaticMissingDeps() []string {
return library.wholeStaticMissingDeps
}
func (library *libraryDecorator) objs() Objects {
return library.objects
}
func (library *libraryDecorator) reuseObjs() Objects {
return library.reuseObjects
}
func (library *libraryDecorator) toc() android.OptionalPath {
return library.tocFile
}
func (library *libraryDecorator) install(ctx ModuleContext, file android.Path) {
if !ctx.static() {
library.baseInstaller.install(ctx, file)
}
}
func (library *libraryDecorator) static() bool {
return library.Properties.VariantIsStatic
}
func (library *libraryDecorator) setStatic(static bool) {
library.Properties.VariantIsStatic = static
}
func NewLibrary(hod android.HostOrDeviceSupported, shared, static bool) (*Module, *libraryDecorator) {
module := newModule(hod, android.MultilibBoth)
library := &libraryDecorator{
Properties: LibraryProperties{
BuildShared: shared,
BuildStatic: static,
},
baseCompiler: NewBaseCompiler(),
baseLinker: NewBaseLinker(),
baseInstaller: NewBaseInstaller("lib", "lib64", InstallInSystem),
sanitize: module.sanitize,
}
module.compiler = library
module.linker = library
module.installer = library
return module, library
}
func linkageMutator(mctx android.BottomUpMutatorContext) {
if m, ok := mctx.Module().(*Module); ok && m.linker != nil {
if library, ok := m.linker.(libraryInterface); ok {
var modules []blueprint.Module
if library.buildStatic() && library.buildShared() {
modules = mctx.CreateLocalVariations("static", "shared")
static := modules[0].(*Module)
shared := modules[1].(*Module)
static.linker.(libraryInterface).setStatic(true)
shared.linker.(libraryInterface).setStatic(false)
if staticCompiler, ok := static.compiler.(*libraryDecorator); ok {
sharedCompiler := shared.compiler.(*libraryDecorator)
if len(staticCompiler.Properties.Static.Cflags) == 0 &&
len(sharedCompiler.Properties.Shared.Cflags) == 0 {
// Optimize out compiling common .o files twice for static+shared libraries
mctx.AddInterVariantDependency(reuseObjTag, shared, static)
sharedCompiler.baseCompiler.Properties.Srcs = nil
sharedCompiler.baseCompiler.Properties.Generated_sources = nil
}
}
} else if library.buildStatic() {
modules = mctx.CreateLocalVariations("static")
modules[0].(*Module).linker.(libraryInterface).setStatic(true)
} else if library.buildShared() {
modules = mctx.CreateLocalVariations("shared")
modules[0].(*Module).linker.(libraryInterface).setStatic(false)
}
}
}
}