Merge "Generate NDK sysroots from the platform build."

This commit is contained in:
Dan Albert 2016-07-28 15:58:51 +00:00 committed by Gerrit Code Review
commit c6b4e452b4
9 changed files with 1129 additions and 17 deletions

View File

@ -134,6 +134,10 @@ bootstrap_go_package {
"cc/toolchain.go",
"cc/util.go",
"cc/ndk_headers.go",
"cc/ndk_library.go",
"cc/ndk_sysroot.go",
"cc/arm_device.go",
"cc/arm64_device.go",
"cc/mips_device.go",

View File

@ -18,6 +18,7 @@ import (
"fmt"
"io"
"path/filepath"
"strconv"
"strings"
"android/soong/android"
@ -183,3 +184,22 @@ func (installer *baseInstaller) AndroidMk(ctx AndroidMkContext, ret *android.And
return nil
})
}
func (c *stubCompiler) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
ret.SubName = "." + strconv.Itoa(c.properties.ApiLevel)
}
func (installer *stubInstaller) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) error {
path, file := filepath.Split(installer.installPath)
stem := strings.TrimSuffix(file, filepath.Ext(file))
fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path)
fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
// Prevent make from installing the libraries to obj/lib (since we have
// dozens of libraries with the same name, they'll clobber each other
// and the real versions of the libraries from the platform).
fmt.Fprintln(w, "LOCAL_COPY_TO_INTERMEDIATE_LIBRARIES := false")
return nil
})
}

113
cc/cc.go
View File

@ -58,6 +58,7 @@ func init() {
// the Go initialization order because this package depends on common, so common's init
// functions will run first.
android.RegisterBottomUpMutator("link", linkageMutator)
android.RegisterBottomUpMutator("ndk_api", ndkApiMutator)
android.RegisterBottomUpMutator("test_per_src", testPerSrcMutator)
android.RegisterBottomUpMutator("deps", depsMutator)
@ -70,6 +71,10 @@ func init() {
var (
HostPrebuiltTag = pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
// These libraries have migrated over to the new ndk_library, which is added
// as a variation dependency via depsMutator.
ndkMigratedLibs = []string{}
)
// Flags used by lots of devices. Putting them in package static variables will save bytes in
@ -555,6 +560,8 @@ var (
crtBeginDepTag = dependencyTag{name: "crtbegin"}
crtEndDepTag = dependencyTag{name: "crtend"}
reuseObjTag = dependencyTag{name: "reuse objects"}
ndkStubDepTag = dependencyTag{name: "ndk stub", library: true}
ndkLateStubDepTag = dependencyTag{name: "ndk late stub", library: true}
)
// Module contains the properties and members used by all C/C++ module types, and implements
@ -883,20 +890,40 @@ func (c *Module) depsMutator(actx android.BottomUpMutatorContext) {
c.Properties.AndroidMkSharedLibs = append(c.Properties.AndroidMkSharedLibs, deps.SharedLibs...)
c.Properties.AndroidMkSharedLibs = append(c.Properties.AndroidMkSharedLibs, deps.LateSharedLibs...)
variantNdkLibs := []string{}
variantLateNdkLibs := []string{}
if ctx.sdk() {
version := "." + ctx.sdkVersion()
version := ctx.sdkVersion()
rewriteNdkLibs := func(list []string) []string {
for i, entry := range list {
// Rewrites the names of shared libraries into the names of the NDK
// libraries where appropriate. This returns two slices.
//
// The first is a list of non-variant shared libraries (either rewritten
// NDK libraries to the modules in prebuilts/ndk, or not rewritten
// because they are not NDK libraries).
//
// The second is a list of ndk_library modules. These need to be
// separated because they are a variation dependency and must be added
// in a different manner.
rewriteNdkLibs := func(list []string) ([]string, []string) {
variantLibs := []string{}
nonvariantLibs := []string{}
for _, entry := range list {
if inList(entry, ndkPrebuiltSharedLibraries) {
list[i] = "ndk_" + entry + version
if !inList(entry, ndkMigratedLibs) {
nonvariantLibs = append(nonvariantLibs, entry+".ndk."+version)
} else {
variantLibs = append(variantLibs, entry+ndkLibrarySuffix)
}
} else {
nonvariantLibs = append(variantLibs, entry)
}
}
return list
return nonvariantLibs, variantLibs
}
deps.SharedLibs = rewriteNdkLibs(deps.SharedLibs)
deps.LateSharedLibs = rewriteNdkLibs(deps.LateSharedLibs)
deps.SharedLibs, variantNdkLibs = rewriteNdkLibs(deps.SharedLibs)
deps.LateSharedLibs, variantLateNdkLibs = rewriteNdkLibs(deps.LateSharedLibs)
}
actx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, wholeStaticDepTag,
@ -935,6 +962,12 @@ func (c *Module) depsMutator(actx android.BottomUpMutatorContext) {
if deps.CrtEnd != "" {
actx.AddDependency(c, crtEndDepTag, deps.CrtEnd)
}
version := ctx.sdkVersion()
actx.AddVariationDependencies([]blueprint.Variation{
{"ndk_api", version}, {"link", "shared"}}, ndkStubDepTag, variantNdkLibs...)
actx.AddVariationDependencies([]blueprint.Variation{
{"ndk_api", version}, {"link", "shared"}}, ndkLateStubDepTag, variantLateNdkLibs...)
}
func depsMutator(ctx android.BottomUpMutatorContext) {
@ -990,6 +1023,11 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
// These are allowed, but don't set sdk_version
return true
}
if _, ok := to.linker.(*stubLinker); ok {
// These aren't real libraries, but are the stub shared libraries that are included in
// the NDK.
return true
}
return to.Properties.Sdk_version != ""
}
@ -1073,9 +1111,9 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
var depPtr *android.Paths
switch tag {
case sharedDepTag, sharedExportDepTag:
case ndkStubDepTag, sharedDepTag, sharedExportDepTag:
depPtr = &depPaths.SharedLibs
case lateSharedDepTag:
case lateSharedDepTag, ndkLateStubDepTag:
depPtr = &depPaths.LateSharedLibs
case staticDepTag, staticExportDepTag:
depPtr = &depPaths.StaticLibs
@ -1190,6 +1228,30 @@ func (compiler *baseCompiler) flags(ctx ModuleContext, flags Flags) Flags {
}...)
}
if ctx.sdk() {
// The NDK headers are installed to a common sysroot. While a more
// typical Soong approach would be to only make the headers for the
// library you're using available, we're trying to emulate the NDK
// behavior here, and the NDK always has all the NDK headers available.
flags.GlobalFlags = append(flags.GlobalFlags,
"-isystem "+getCurrentIncludePath(ctx).String(),
"-isystem "+getCurrentIncludePath(ctx).Join(ctx, toolchain.ClangTriple()).String())
// Traditionally this has come from android/api-level.h, but with the
// libc headers unified it must be set by the build system since we
// don't have per-API level copies of that header now.
flags.GlobalFlags = append(flags.GlobalFlags,
"-D__ANDROID_API__="+ctx.sdkVersion())
// Until the full NDK has been migrated to using ndk_headers, we still
// need to add the legacy sysroot includes to get the full set of
// headers.
legacyIncludes := fmt.Sprintf(
"prebuilts/ndk/current/platforms/android-%s/arch-%s/usr/include",
ctx.sdkVersion(), ctx.Arch().ArchType.String())
flags.GlobalFlags = append(flags.GlobalFlags, "-isystem "+legacyIncludes)
}
instructionSet := compiler.Properties.Instruction_set
if flags.RequiredInstructionSet != "" {
instructionSet = flags.RequiredInstructionSet
@ -1310,11 +1372,22 @@ func (compiler *baseCompiler) flags(ctx ModuleContext, flags Flags) Flags {
return flags
}
func ndkPathDeps(ctx ModuleContext) android.Paths {
if ctx.sdk() {
// The NDK sysroot timestamp file depends on all the NDK sysroot files
// (headers and libraries).
return android.Paths{getNdkSysrootTimestampFile(ctx)}
}
return nil
}
func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Paths {
pathDeps := deps.GeneratedHeaders
pathDeps = append(pathDeps, ndkPathDeps(ctx)...)
// Compile files listed in c.Properties.Srcs into objects
objFiles := compiler.compileObjs(ctx, flags, "",
compiler.Properties.Srcs, compiler.Properties.Exclude_srcs,
deps.GeneratedSources, deps.GeneratedHeaders)
deps.GeneratedSources, pathDeps)
if ctx.Failed() {
return nil
@ -1595,14 +1668,17 @@ func (library *libraryCompiler) compile(ctx ModuleContext, flags Flags, deps Pat
objFiles = library.baseCompiler.compile(ctx, flags, deps)
library.reuseObjFiles = objFiles
pathDeps := deps.GeneratedHeaders
pathDeps = append(pathDeps, ndkPathDeps(ctx)...)
if library.linker.static() {
objFiles = append(objFiles, library.compileObjs(ctx, flags, android.DeviceStaticLibrary,
library.Properties.Static.Srcs, library.Properties.Static.Exclude_srcs,
nil, deps.GeneratedHeaders)...)
nil, pathDeps)...)
} else {
objFiles = append(objFiles, library.compileObjs(ctx, flags, android.DeviceSharedLibrary,
library.Properties.Shared.Srcs, library.Properties.Shared.Exclude_srcs,
nil, deps.GeneratedHeaders)...)
nil, pathDeps)...)
}
return objFiles
@ -1626,6 +1702,10 @@ type libraryLinker struct {
// For whole_static_libs
objFiles android.Paths
// Uses the module's name if empty, but can be overridden. Does not include
// shlib suffix.
libName string
}
var _ linker = (*libraryLinker)(nil)
@ -1646,7 +1726,10 @@ func (library *libraryLinker) props() []interface{} {
}
func (library *libraryLinker) getLibName(ctx ModuleContext) string {
name := ctx.ModuleName()
name := library.libName
if name == "" {
name = ctx.ModuleName()
}
if ctx.Host() && Bool(library.Properties.Unique_host_soname) {
if !strings.HasSuffix(name, "-host") {
@ -2634,10 +2717,6 @@ func ndkPrebuiltLibraryFactory() (blueprint.Module, []interface{}) {
func (ndk *ndkPrebuiltLibraryLinker) link(ctx ModuleContext, flags Flags,
deps PathDeps, objFiles android.Paths) android.Path {
// A null build step, but it sets up the output path.
if !strings.HasPrefix(ctx.ModuleName(), "ndk_lib") {
ctx.ModuleErrorf("NDK prebuilts must have an ndk_lib prefixed name")
}
ndk.exportIncludes(ctx, "-isystem")
return ndkPrebuiltModuleToPath(ctx, flags.Toolchain, flags.Toolchain.ShlibSuffix(),

262
cc/gen_stub_libs.py Executable file
View File

@ -0,0 +1,262 @@
#!/usr/bin/env python
#
# Copyright (C) 2016 The Android Open Source Project
#
# 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.
#
"""Generates source for stub shared libraries for the NDK."""
import argparse
import os
import re
ALL_ARCHITECTURES = (
'arm',
'arm64',
'mips',
'mips64',
'x86',
'x86_64',
)
class Scope(object):
"""Enum for version script scope.
Top: Top level of the file.
Global: In a version and visibility section where symbols should be visible
to the NDK.
Local: In a visibility section of a public version where symbols should be
hidden to the NDK.
Private: In a version where symbols should not be visible to the NDK.
"""
Top = 1
Global = 2
Local = 3
Private = 4
class Stack(object):
"""Basic stack implementation."""
def __init__(self):
self.stack = []
def push(self, obj):
"""Push an item on to the stack."""
self.stack.append(obj)
def pop(self):
"""Remove and return the item on the top of the stack."""
return self.stack.pop()
@property
def top(self):
"""Return the top of the stack."""
return self.stack[-1]
def version_is_private(version):
"""Returns True if the version name should be treated as private."""
return version.endswith('_PRIVATE') or version.endswith('_PLATFORM')
def enter_version(scope, line, version_file):
"""Enters a new version block scope."""
if scope.top != Scope.Top:
raise RuntimeError('Encountered nested version block.')
# Entering a new version block. By convention symbols with versions ending
# with "_PRIVATE" or "_PLATFORM" are not included in the NDK.
version_name = line.split('{')[0].strip()
if version_is_private(version_name):
scope.push(Scope.Private)
else:
scope.push(Scope.Global) # By default symbols are visible.
version_file.write(line)
def leave_version(scope, line, version_file):
"""Leave a version block scope."""
# There is no close to a visibility section, just the end of the version or
# a new visiblity section.
assert scope.top in (Scope.Global, Scope.Local, Scope.Private)
if scope.top != Scope.Private:
version_file.write(line)
scope.pop()
assert scope.top == Scope.Top
def enter_visibility(scope, line, version_file):
"""Enters a new visibility block scope."""
leave_visibility(scope)
version_file.write(line)
visibility = line.split(':')[0].strip()
if visibility == 'local':
scope.push(Scope.Local)
elif visibility == 'global':
scope.push(Scope.Global)
else:
raise RuntimeError('Unknown visiblity label: ' + visibility)
def leave_visibility(scope):
"""Leaves a visibility block scope."""
assert scope.top in (Scope.Global, Scope.Local)
scope.pop()
assert scope.top == Scope.Top
def handle_top_scope(scope, line, version_file):
"""Processes a line in the top level scope."""
if '{' in line:
enter_version(scope, line, version_file)
else:
raise RuntimeError('Unexpected contents at top level: ' + line)
def handle_private_scope(scope, line, version_file):
"""Eats all input."""
if '}' in line:
leave_version(scope, line, version_file)
def handle_local_scope(scope, line, version_file):
"""Passes through input."""
if ':' in line:
enter_visibility(scope, line, version_file)
elif '}' in line:
leave_version(scope, line, version_file)
else:
version_file.write(line)
def symbol_in_arch(tags, arch):
"""Returns true if the symbol is present for the given architecture."""
has_arch_tags = False
for tag in tags:
if tag == arch:
return True
if tag in ALL_ARCHITECTURES:
has_arch_tags = True
# If there were no arch tags, the symbol is available for all
# architectures. If there were any arch tags, the symbol is only available
# for the tagged architectures.
return not has_arch_tags
def symbol_in_version(tags, arch, version):
"""Returns true if the symbol is present for the given version."""
introduced_tag = None
arch_specific = False
for tag in tags:
# If there is an arch-specific tag, it should override the common one.
if tag.startswith('introduced=') and not arch_specific:
introduced_tag = tag
elif tag.startswith('introduced-' + arch + '='):
introduced_tag = tag
arch_specific = True
if introduced_tag is None:
# We found no "introduced" tags, so the symbol has always been
# available.
return True
# The tag is a key=value pair, and we only care about the value now.
_, _, version_str = introduced_tag.partition('=')
return version >= int(version_str)
def handle_global_scope(scope, line, src_file, version_file, arch, api):
"""Emits present symbols to the version file and stub source file."""
if ':' in line:
enter_visibility(scope, line, version_file)
return
if '}' in line:
leave_version(scope, line, version_file)
return
if ';' not in line:
raise RuntimeError('Expected ; to terminate symbol: ' + line)
if '*' in line:
raise RuntimeError('Wildcard global symbols are not permitted.')
# Line is now in the format "<symbol-name>; # tags"
# Tags are whitespace separated.
symbol_name, _, rest = line.strip().partition(';')
_, _, all_tags = rest.partition('#')
tags = re.split(r'\s+', all_tags)
if not symbol_in_arch(tags, arch):
return
if not symbol_in_version(tags, arch, api):
return
if 'var' in tags:
src_file.write('int {} = 0;\n'.format(symbol_name))
else:
src_file.write('void {}() {{}}\n'.format(symbol_name))
version_file.write(line)
def generate(symbol_file, src_file, version_file, arch, api):
"""Generates the stub source file and version script."""
scope = Stack()
scope.push(Scope.Top)
for line in symbol_file:
if line.strip() == '' or line.strip().startswith('#'):
version_file.write(line)
elif scope.top == Scope.Top:
handle_top_scope(scope, line, version_file)
elif scope.top == Scope.Private:
handle_private_scope(scope, line, version_file)
elif scope.top == Scope.Local:
handle_local_scope(scope, line, version_file)
elif scope.top == Scope.Global:
handle_global_scope(scope, line, src_file, version_file, arch, api)
def parse_args():
"""Parses and returns command line arguments."""
parser = argparse.ArgumentParser()
parser.add_argument('--api', type=int, help='API level being targeted.')
parser.add_argument(
'--arch', choices=ALL_ARCHITECTURES,
help='Architecture being targeted.')
parser.add_argument(
'symbol_file', type=os.path.realpath, help='Path to symbol file.')
parser.add_argument(
'stub_src', type=os.path.realpath,
help='Path to output stub source file.')
parser.add_argument(
'version_script', type=os.path.realpath,
help='Path to output version script.')
return parser.parse_args()
def main():
"""Program entry point."""
args = parse_args()
with open(args.symbol_file) as symbol_file:
with open(args.stub_src, 'w') as src_file:
with open(args.version_script, 'w') as version_file:
generate(symbol_file, src_file, version_file, args.arch,
args.api)
if __name__ == '__main__':
main()

View File

@ -50,6 +50,8 @@ func makeVarsProvider(ctx android.MakeVarsContext) {
ctx.StrictRaw("SRC_HEADERS", strings.Join(includes, " "))
ctx.StrictRaw("SRC_SYSTEM_HEADERS", strings.Join(systemIncludes, " "))
ctx.Strict("NDK_MIGRATED_LIBS", strings.Join(ndkMigratedLibs, " "))
hostTargets := ctx.Config().Targets[android.Host]
makeVarsToolchain(ctx, "", hostTargets[0])
if len(hostTargets) > 1 {
@ -198,6 +200,7 @@ func makeVarsToolchain(ctx android.MakeVarsContext, secondPrefix string,
ctx.Strict(makePrefix+"STRIP", gccCmd(toolchain, "strip"))
ctx.Strict(makePrefix+"GCC_VERSION", toolchain.GccVersion())
ctx.Strict(makePrefix+"NDK_GCC_VERSION", toolchain.GccVersion())
ctx.Strict(makePrefix+"NDK_TRIPLE", toolchain.ClangTriple())
}
ctx.Strict(makePrefix+"TOOLCHAIN_ROOT", toolchain.GccRoot())

102
cc/ndk_headers.go Normal file
View File

@ -0,0 +1,102 @@
// 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 (
"path/filepath"
"github.com/google/blueprint"
"android/soong/android"
)
// Returns the NDK base include path for use with sdk_version current. Usable with -I.
func getCurrentIncludePath(ctx android.ModuleContext) android.OutputPath {
return getNdkSysrootBase(ctx).Join(ctx, "usr/include")
}
type headerProperies struct {
// Base directory of the headers being installed. As an example:
//
// ndk_headers {
// name: "foo",
// from: "include",
// to: "",
// srcs: ["include/foo/bar/baz.h"],
// }
//
// Will install $SYSROOT/usr/include/foo/bar/baz.h. If `from` were instead
// "include/foo", it would have installed $SYSROOT/usr/include/bar/baz.h.
From string
// Install path within the sysroot. This is relative to usr/include.
To string
// List of headers to install. Glob compatible. Common case is "include/**/*.h".
Srcs []string
}
type headerModule struct {
android.ModuleBase
properties headerProperies
installPaths []string
}
func (m *headerModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
srcFiles := ctx.ExpandSources(m.properties.Srcs, nil)
for _, header := range srcFiles {
// Output path is the sysroot base + "usr/include" + to directory + directory component
// of the file without the leading from directory stripped.
//
// Given:
// sysroot base = "ndk/sysroot"
// from = "include/foo"
// to = "bar"
// header = "include/foo/woodly/doodly.h"
// output path = "ndk/sysroot/usr/include/bar/woodly/doodly.h"
// full/platform/path/to/include/foo
fullFromPath := android.PathForModuleSrc(ctx, m.properties.From)
// full/platform/path/to/include/foo/woodly
headerDir := filepath.Dir(header.String())
// woodly
strippedHeaderDir, err := filepath.Rel(fullFromPath.String(), headerDir)
if err != nil {
ctx.ModuleErrorf("filepath.Rel(%q, %q) failed: %s", headerDir,
fullFromPath.String(), err)
}
// full/platform/path/to/sysroot/usr/include/bar/woodly
installDir := getCurrentIncludePath(ctx).Join(ctx, m.properties.To, strippedHeaderDir)
// full/platform/path/to/sysroot/usr/include/bar/woodly/doodly.h
installPath := ctx.InstallFile(installDir, header)
m.installPaths = append(m.installPaths, installPath.String())
}
if len(m.installPaths) == 0 {
ctx.ModuleErrorf("srcs %q matched zero files", m.properties.Srcs)
}
}
func ndkHeadersFactory() (blueprint.Module, []interface{}) {
module := &headerModule{}
return android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst,
&module.properties)
}

250
cc/ndk_library.go Normal file
View File

@ -0,0 +1,250 @@
// 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 (
"fmt"
"strconv"
"strings"
"github.com/google/blueprint"
"android/soong/android"
)
var (
toolPath = pctx.SourcePathVariable("toolPath", "build/soong/cc/gen_stub_libs.py")
genStubSrc = pctx.StaticRule("genStubSrc",
blueprint.RuleParams{
Command: "$toolPath --arch $arch --api $apiLevel $in $out",
Description: "genStubSrc $out",
CommandDeps: []string{"$toolPath"},
}, "arch", "apiLevel")
ndkLibrarySuffix = ".ndk"
)
// Creates a stub shared library based on the provided version file.
//
// The name of the generated file will be based on the module name by stripping
// the ".ndk" suffix from the module name. Module names must end with ".ndk"
// (as a convention to allow soong to guess the NDK name of a dependency when
// needed). "libfoo.ndk" will generate "libfoo.so.
//
// Example:
//
// ndk_library {
// name: "libfoo.ndk",
// symbol_file: "libfoo.map.txt",
// first_version: "9",
// }
//
type libraryProperties struct {
// Relative path to the symbol map.
// An example file can be seen here: TODO(danalbert): Make an example.
Symbol_file string
// The first API level a library was available. A library will be generated
// for every API level beginning with this one.
First_version string
// Private property for use by the mutator that splits per-API level.
ApiLevel int `blueprint:"mutated"`
}
type stubCompiler struct {
baseCompiler
properties libraryProperties
}
// OMG GO
func intMin(a int, b int) int {
if a < b {
return a
} else {
return b
}
}
func generateStubApiVariants(mctx android.BottomUpMutatorContext, c *stubCompiler) {
minVersion := 9 // Minimum version supported by the NDK.
// TODO(danalbert): Use PlatformSdkVersion when possible.
// This is an interesting case because for the moment we actually need 24
// even though the latest released version in aosp is 23. prebuilts/ndk/r11
// has android-24 versions of libraries, and as platform libraries get
// migrated the libraries in prebuilts will need to depend on them.
//
// Once everything is all moved over to the new stuff (when there isn't a
// prebuilts/ndk any more) then this should be fixable, but for now I think
// it needs to remain as-is.
maxVersion := 24
firstArchVersions := map[string]int{
"arm": 9,
"arm64": 21,
"mips": 9,
"mips64": 21,
"x86": 9,
"x86_64": 21,
}
// If the NDK drops support for a platform version, we don't want to have to
// fix up every module that was using it as its minimum version. Clip to the
// supported version here instead.
firstVersion, err := strconv.Atoi(c.properties.First_version)
if err != nil {
mctx.ModuleErrorf("Invalid first_version value (must be int): %q",
c.properties.First_version)
}
if firstVersion < minVersion {
firstVersion = minVersion
}
arch := mctx.Arch().ArchType.String()
firstArchVersion, ok := firstArchVersions[arch]
if !ok {
panic(fmt.Errorf("Arch %q not found in firstArchVersions", arch))
}
firstGenVersion := intMin(firstVersion, firstArchVersion)
versionStrs := make([]string, maxVersion-firstGenVersion+1)
for version := firstGenVersion; version <= maxVersion; version++ {
versionStrs[version-firstGenVersion] = strconv.Itoa(version)
}
modules := mctx.CreateVariations(versionStrs...)
for i, module := range modules {
module.(*Module).compiler.(*stubCompiler).properties.ApiLevel = firstGenVersion + i
}
}
func ndkApiMutator(mctx android.BottomUpMutatorContext) {
if m, ok := mctx.Module().(*Module); ok {
if compiler, ok := m.compiler.(*stubCompiler); ok {
generateStubApiVariants(mctx, compiler)
}
}
}
func (c *stubCompiler) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Paths {
arch := ctx.Arch().ArchType.String()
if !strings.HasSuffix(ctx.ModuleName(), ndkLibrarySuffix) {
ctx.ModuleErrorf("ndk_library modules names must be suffixed with %q\n",
ndkLibrarySuffix)
}
libName := strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix)
fileBase := fmt.Sprintf("%s.%s.%d", libName, arch, c.properties.ApiLevel)
stubSrcName := fileBase + ".c"
stubSrcPath := android.PathForModuleGen(ctx, stubSrcName)
versionScriptName := fileBase + ".map"
versionScriptPath := android.PathForModuleGen(ctx, versionScriptName)
symbolFilePath := android.PathForModuleSrc(ctx, c.properties.Symbol_file)
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: genStubSrc,
Outputs: []android.WritablePath{stubSrcPath, versionScriptPath},
Input: symbolFilePath,
Args: map[string]string{
"arch": arch,
"apiLevel": strconv.Itoa(c.properties.ApiLevel),
},
})
flags.CFlags = append(flags.CFlags,
// We're knowingly doing some otherwise unsightly things with builtin
// functions here. We're just generating stub libraries, so ignore it.
"-Wno-incompatible-library-redeclaration",
"-Wno-builtin-requires-header",
"-Wno-invalid-noreturn",
// These libraries aren't actually used. Don't worry about unwinding
// (avoids the need to link an unwinder into a fake library).
"-fno-unwind-tables",
)
subdir := ""
srcs := []string{}
excludeSrcs := []string{}
extraSrcs := []android.Path{stubSrcPath}
extraDeps := []android.Path{}
return c.baseCompiler.compileObjs(ctx, flags, subdir, srcs, excludeSrcs,
extraSrcs, extraDeps)
}
type stubLinker struct {
libraryLinker
}
func (linker *stubLinker) deps(ctx BaseModuleContext, deps Deps) Deps {
return Deps{}
}
func (linker *stubLinker) flags(ctx ModuleContext, flags Flags) Flags {
linker.libraryLinker.libName = strings.TrimSuffix(ctx.ModuleName(),
ndkLibrarySuffix)
return linker.libraryLinker.flags(ctx, flags)
}
type stubInstaller struct {
baseInstaller
compiler *stubCompiler
installPath string
}
var _ installer = (*stubInstaller)(nil)
func (installer *stubInstaller) install(ctx ModuleContext, path android.Path) {
arch := ctx.Target().Arch.ArchType.Name
apiLevel := installer.compiler.properties.ApiLevel
// arm64 isn't actually a multilib toolchain, so unlike the other LP64
// architectures it's just installed to lib.
libDir := "lib"
if ctx.toolchain().Is64Bit() && arch != "arm64" {
libDir = "lib64"
}
installDir := getNdkInstallBase(ctx).Join(ctx, fmt.Sprintf(
"platforms/android-%d/arch-%s/usr/%s", apiLevel, arch, libDir))
installer.installPath = ctx.InstallFile(installDir, path).String()
}
func newStubLibrary() *Module {
module := newModule(android.DeviceSupported, android.MultilibBoth)
module.stl = nil
linker := &stubLinker{}
linker.dynamicProperties.BuildShared = true
linker.dynamicProperties.BuildStatic = false
linker.stripper.StripProperties.Strip.None = true
module.linker = linker
compiler := &stubCompiler{}
module.compiler = compiler
module.installer = &stubInstaller{baseInstaller{
dir: "lib",
dir64: "lib64",
}, compiler, ""}
return module
}
func ndkLibraryFactory() (blueprint.Module, []interface{}) {
module := newStubLibrary()
return android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth,
&module.compiler.(*stubCompiler).properties)
}

112
cc/ndk_sysroot.go Normal file
View File

@ -0,0 +1,112 @@
// 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
// The platform needs to provide the following artifacts for the NDK:
// 1. Bionic headers.
// 2. Platform API headers.
// 3. NDK stub shared libraries.
// 4. Bionic static libraries.
//
// TODO(danalbert): All of the above need to include NOTICE files.
//
// Components 1 and 2: Headers
// The bionic and platform API headers are generalized into a single
// `ndk_headers` rule. This rule has a `from` property that indicates a base
// directory from which headers are to be taken, and a `to` property that
// indicates where in the sysroot they should reside relative to usr/include.
// There is also a `srcs` property that is glob compatible for specifying which
// headers to include.
//
// Component 3: Stub Libraries
// The shared libraries in the NDK are not the actual shared libraries they
// refer to (to prevent people from accidentally loading them), but stub
// libraries with dummy implementations of everything for use at build time
// only.
//
// Since we don't actually need to know anything about the stub libraries aside
// from a list of functions and globals to be exposed, we can create these for
// every platform level in the current tree. This is handled by the
// ndk_library rule.
//
// Component 4: Static Libraries
// The NDK only provides static libraries for bionic, not the platform APIs.
// Since these need to be the actual implementation, we can't build old versions
// in the current platform tree. As such, legacy versions are checked in
// prebuilt to development/ndk, and a current version is built and archived as
// part of the platform build. The platfrom already builds these libraries, our
// NDK build rules only need to archive them for retrieval so they can be added
// to the prebuilts.
//
// TODO(danalbert): Write `ndk_static_library` rule.
import (
"github.com/google/blueprint"
"android/soong"
"android/soong/android"
)
func init() {
soong.RegisterModuleType("ndk_headers", ndkHeadersFactory)
soong.RegisterModuleType("ndk_library", ndkLibraryFactory)
soong.RegisterSingletonType("ndk", NdkSingleton)
pctx.Import("android/soong/common")
}
func getNdkInstallBase(ctx android.ModuleContext) android.OutputPath {
return android.PathForOutput(ctx, "ndk")
}
// Returns the main install directory for the NDK sysroot. Usable with --sysroot.
func getNdkSysrootBase(ctx android.ModuleContext) android.OutputPath {
return getNdkInstallBase(ctx).Join(ctx, "sysroot")
}
func getNdkSysrootTimestampFile(ctx android.PathContext) android.Path {
return android.PathForOutput(ctx, "ndk.timestamp")
}
func NdkSingleton() blueprint.Singleton {
return &ndkSingleton{}
}
type ndkSingleton struct{}
func (n *ndkSingleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
installPaths := []string{}
ctx.VisitAllModules(func(module blueprint.Module) {
if m, ok := module.(*headerModule); ok {
installPaths = append(installPaths, m.installPaths...)
}
})
ctx.VisitAllModules(func(module blueprint.Module) {
if m, ok := module.(*Module); ok {
if installer, ok := m.installer.(*stubInstaller); ok {
installPaths = append(installPaths, installer.installPath)
}
}
})
// There's a dummy "ndk" rule defined in ndk/Android.mk that depends on
// this. `m ndk` will build the sysroots.
ctx.Build(pctx, blueprint.BuildParams{
Rule: android.Touch,
Outputs: []string{getNdkSysrootTimestampFile(ctx).String()},
Implicits: installPaths,
})
}

280
cc/pylintrc Normal file
View File

@ -0,0 +1,280 @@
[MASTER]
# Specify a configuration file.
#rcfile=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Profiled execution.
profile=no
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=CVS
# Pickle collected data for later comparisons.
persistent=yes
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
[MESSAGES CONTROL]
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time. See also the "--disable" option for examples.
#enable=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once).You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
disable=design
[REPORTS]
# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html. You can also give a reporter class, eg
# mypackage.mymodule.MyReporterClass.
output-format=text
# Put messages in a separate file for each module / package specified on the
# command line instead of printing them on stdout. Reports (if any) will be
# written in a file name "pylint_global.[txt|html]".
files-output=no
# Tells whether to display a full report or only the messages
reports=yes
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Add a comment according to your evaluation note. This is used by the global
# evaluation report (RP0004).
comment=no
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details
#msg-template=
[BASIC]
# Required attributes for module, separated by a comma
required-attributes=
# List of builtins function names that should not be used, separated by a comma
bad-functions=map,filter,apply,input
# Regular expression which should only match correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Regular expression which should only match correct module level names
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Regular expression which should only match correct class names
class-rgx=[A-Z_][a-zA-Z0-9]+$
# Regular expression which should only match correct function names
function-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct method names
method-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct instance attribute names
attr-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct argument names
argument-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct variable names
variable-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct attribute names in class
# bodies
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Regular expression which should only match correct list comprehension /
# generator expression variable names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=__.*__
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
[TYPECHECK]
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# List of classes names for which member attributes should not be checked
# (useful for classes with attributes dynamically set).
ignored-classes=SQLObject
# When zope mode is activated, add a predefined set of Zope acquired attributes
# to generated-members.
zope=no
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E0201 when accessed. Python regular
# expressions are accepted.
generated-members=REQUEST,acl_users,aq_parent
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,XXX,TODO
[SIMILARITIES]
# Minimum lines number of a similarity.
min-similarity-lines=4
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
# Ignore imports when computing similarities.
ignore-imports=no
[VARIABLES]
# Tells whether we should check for unused import in __init__ files.
init-import=no
# A regular expression matching the beginning of the name of dummy variables
# (i.e. not used).
dummy-variables-rgx=_|dummy
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=
[FORMAT]
# Maximum number of characters on a single line.
max-line-length=80
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
# List of optional constructs for which whitespace checking is disabled
no-space-check=trailing-comma,dict-separator
# Maximum number of lines in a module
max-module-lines=1000
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
[IMPORTS]
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=regsub,TERMIOS,Bastion,rexec
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=
[DESIGN]
# Maximum number of arguments for function / method
max-args=5
# Argument names that match this expression will be ignored. Default to name
# with leading underscore
ignored-argument-names=_.*
# Maximum number of locals for function / method body
max-locals=15
# Maximum number of return / yield for function / method body
max-returns=6
# Maximum number of branch for function / method body
max-branches=12
# Maximum number of statements in function / method body
max-statements=50
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
[CLASSES]
# List of interface methods to ignore, separated by a comma. This is used for
# instance to not check methods defines in Zope's Interface base class.
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=mcs
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception