aboutsummaryrefslogtreecommitdiffstats
path: root/cc
diff options
context:
space:
mode:
authorDan Albert <danalbert@google.com>2016-06-17 16:45:24 -0700
committerDan Albert <danalbert@google.com>2016-07-28 00:42:05 -0700
commit914449fca8c749588e28dfdfafc3226aa407c47e (patch)
tree1f70761b0869ff651676ced2293b530dd7f3e3e6 /cc
parentfb8f9a800ed42e548893b13ebc3840fc31aaae94 (diff)
downloadbuild_soong-914449fca8c749588e28dfdfafc3226aa407c47e.tar.gz
build_soong-914449fca8c749588e28dfdfafc3226aa407c47e.tar.bz2
build_soong-914449fca8c749588e28dfdfafc3226aa407c47e.zip
Generate NDK sysroots from the platform build.
The list of migrated libraries is currently empty. Libraries will be migrated as follow up patches. Test: Migrated libc to this system and everything still builds. build.ninja shows libraries being built and used and headers are collected for the sysroot. Bug: http://b/27533932 Change-Id: Iaba00543c1390f432befe0eed768ed3fbb8a9b96
Diffstat (limited to 'cc')
-rw-r--r--cc/androidmk.go20
-rw-r--r--cc/cc.go115
-rwxr-xr-xcc/gen_stub_libs.py262
-rw-r--r--cc/makevars.go3
-rw-r--r--cc/ndk_headers.go102
-rw-r--r--cc/ndk_library.go250
-rw-r--r--cc/ndk_sysroot.go112
-rw-r--r--cc/pylintrc280
8 files changed, 1126 insertions, 18 deletions
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 4f76dc9e..df44a4c2 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -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
+ })
+}
diff --git a/cc/cc.go b/cc/cc.go
index 60bc4d47..12d9ed71 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -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()
-
- rewriteNdkLibs := func(list []string) []string {
- for i, entry := range list {
+ version := ctx.sdkVersion()
+
+ // 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(),
diff --git a/cc/gen_stub_libs.py b/cc/gen_stub_libs.py
new file mode 100755
index 00000000..a4a04214
--- /dev/null
+++ b/cc/gen_stub_libs.py
@@ -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()
diff --git a/cc/makevars.go b/cc/makevars.go
index 0e75329e..ae95a189 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -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())
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
new file mode 100644
index 00000000..5d70b891
--- /dev/null
+++ b/cc/ndk_headers.go
@@ -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)
+}
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
new file mode 100644
index 00000000..562186ea
--- /dev/null
+++ b/cc/ndk_library.go
@@ -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)
+}
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
new file mode 100644
index 00000000..2eae360c
--- /dev/null
+++ b/cc/ndk_sysroot.go
@@ -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,
+ })
+}
diff --git a/cc/pylintrc b/cc/pylintrc
new file mode 100644
index 00000000..5d1aa9af
--- /dev/null
+++ b/cc/pylintrc
@@ -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