Merge "Add a test case for mixed ARM and thumb LTO."
diff --git a/.gitignore b/.gitignore
index 1bcd0ed..8d5da0a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
*~
*.bak
+*.pk1
Thumbs.db
local.properties
build.xml
diff --git a/build/cmake/android.toolchain.cmake b/build/cmake/android.toolchain.cmake
index 5e6510d..971bb94 100644
--- a/build/cmake/android.toolchain.cmake
+++ b/build/cmake/android.toolchain.cmake
@@ -165,6 +165,8 @@
set(ANDROID_ABI armeabi-v7a)
endif()
if(ANDROID_PLATFORM MATCHES "^android-([0-9]|1[0-3])$")
+ message(WARNING "${ANDROID_PLATFORM} is unsupported. Using minimum supported "
+ "version android-14")
set(ANDROID_PLATFORM android-14)
elseif(ANDROID_PLATFORM STREQUAL android-20)
set(ANDROID_PLATFORM android-19)
diff --git a/checkbuild.py b/checkbuild.py
index 86b48d9..f9fc55d 100755
--- a/checkbuild.py
+++ b/checkbuild.py
@@ -1446,8 +1446,11 @@
install_dir = ndk.paths.get_install_path(out_dir)
path = os.path.join(install_dir, self.path)
with open(path, 'w') as source_properties:
+ build = args.build_number
+ if build == 'dev':
+ build = '0'
version = '{}.{}.{}'.format(
- ndk.config.major, ndk.config.hotfix, args.build_number)
+ ndk.config.major, ndk.config.hotfix, build)
if ndk.config.beta > 0:
version += '-beta{}'.format(ndk.config.beta)
source_properties.writelines([
@@ -1697,7 +1700,9 @@
required_package_modules = set(get_all_module_names())
have_required_modules = required_package_modules <= set(module_names)
- do_package = have_required_modules if args.package else args.force_package
+ do_package = have_required_modules if args.package else False
+ if args.force_package:
+ do_package = True
# TODO(danalbert): wine?
# We're building the Windows packages from Linux, so we can't actually run
diff --git a/ndk/ansi.py b/ndk/ansi.py
index 50719a9..82e85bd 100644
--- a/ndk/ansi.py
+++ b/ndk/ansi.py
@@ -43,7 +43,7 @@
def goto_first_column():
- return '\r'
+ return '\033[1G'
def clear_line():
diff --git a/ndk/run_tests.py b/ndk/run_tests.py
index b081cab..340a397 100755
--- a/ndk/run_tests.py
+++ b/ndk/run_tests.py
@@ -652,6 +652,10 @@
help='Path to the config file describing the test run.')
build_options = parser.add_argument_group('Build Options')
+ build_options.add_argument(
+ '--build-report', type=os.path.realpath,
+ help='Write the build report to the given path.')
+
build_exclusive_group = build_options.add_mutually_exclusive_group()
build_exclusive_group.add_argument(
'--rebuild', action='store_true',
diff --git a/ndk/test/builder.py b/ndk/test/builder.py
index 83a62d5..af5322c 100644
--- a/ndk/test/builder.py
+++ b/ndk/test/builder.py
@@ -21,6 +21,7 @@
import logging
import multiprocessing
import os
+import pickle
import shutil
import ndk.abis
@@ -96,6 +97,11 @@
return runner
+def write_build_report(build_report, results):
+ with open(build_report, 'w') as build_report_file:
+ pickle.dump(results, build_report_file)
+
+
class TestBuilder(object):
def __init__(self, test_spec, test_options, printer):
self.runner = build_test_runner(test_spec, test_options, printer)
@@ -129,7 +135,10 @@
test_filters = filters.TestFilter.from_string(
self.test_options.test_filter)
- return self.runner.run(self.obj_dir, self.dist_dir, test_filters)
+ result = self.runner.run(self.obj_dir, self.dist_dir, test_filters)
+ if self.test_options.build_report:
+ write_build_report(self.test_options.build_report, result)
+ return result
class LoadRestrictingWorkQueue(object):
diff --git a/ndk/test/spec.py b/ndk/test/spec.py
index 62a0cb6..36e0a8d 100644
--- a/ndk/test/spec.py
+++ b/ndk/test/spec.py
@@ -19,7 +19,7 @@
class TestOptions(object):
"""Configuration for how tests should be run."""
def __init__(self, src_dir, ndk_path, out_dir, test_filter=None,
- clean=True):
+ clean=True, build_report=None):
"""Initializes a TestOptions object.
Args:
@@ -28,12 +28,14 @@
out_dir: Test output directory.
test_filter: Test filter string.
clean: True if the out directory should be cleaned before building.
+ build_report: Path to write a build report to, if any.
"""
self.src_dir = src_dir
self.ndk_path = ndk_path
self.out_dir = out_dir
self.test_filter = test_filter
self.clean = clean
+ self.build_report = build_report
class TestSpec(object):
diff --git a/parse_elfnote.py b/parse_elfnote.py
old mode 100644
new mode 100755
index 0c73b22..b5f557e
--- a/parse_elfnote.py
+++ b/parse_elfnote.py
@@ -15,16 +15,25 @@
# limitations under the License.
#
+#
+# Dump the contents of the .note.android.ident section, a NOTE section
+# embedded into Android binaries. See here:
+# - master: ndk/sources/crt/crtbrand.S
+# - master: bionic/libc/arch-common/bionic/crtbrand.S
+# - NDK before r14: development/ndk/platforms/common/src/crtbrand.c
+#
+# Note sections can also be dumped with `readelf -n`.
+#
+
+from __future__ import division, print_function
import argparse
import logging
+import struct
import subprocess
import sys
-from ctypes import c_char
-from ctypes import c_int
-from ctypes import Structure
+from StringIO import StringIO
SEC_NAME = '.note.android.ident'
-ABI_VENDOR = 'Android'
NDK_RESERVED_SIZE = 64
@@ -33,21 +42,71 @@
return logging.getLogger(__name__)
-class AbiTag(Structure):
- _fields_ = [('namesz', c_int),
- ('descsz', c_int),
- ('type', c_int),
- ('name', c_char * len(ABI_VENDOR)),
- ('android_api', c_int),
- ('ndk_version', c_char * NDK_RESERVED_SIZE),
- ('ndk_build_number', c_char * NDK_RESERVED_SIZE)]
+def round_up_to_nearest(val, step):
+ """Round an integer, val, to the next multiple of a positive integer,
+ step."""
+ return (val + (step - 1)) // step * step
+
+
+class StructParser(object):
+ def __init__(self, buf):
+ self._sio = StringIO(buf)
+
+ @property
+ def _remaining(self):
+ return self._sio.len - self._sio.pos
+
+ @property
+ def empty(self):
+ return self._remaining == 0
+
+ def read_struct(self, fmt, kind):
+ fmt = struct.Struct(fmt)
+ if self._remaining < fmt.size:
+ sys.exit('error: {} was truncated'.format(kind))
+ return fmt.unpack(self._sio.read(fmt.size))
+
+
+def iterate_notes(sec_data):
+ sec_data = StructParser(sec_data)
+ while not sec_data.empty:
+ (namesz, descsz, kind) = sec_data.read_struct('<III', 'note header')
+ (name, desc) = sec_data.read_struct(
+ '{}s{}s'.format(
+ round_up_to_nearest(namesz, 4),
+ round_up_to_nearest(descsz, 4)),
+ 'note body')
+ name = name[:namesz]
+ if len(name) > 0:
+ if name[-1:] == '\0':
+ name = name[:-1]
+ else:
+ logger().warning('note name %s isn\'t NUL-terminated', name)
+ yield name, kind, desc[:descsz]
+
+
+def dump_android_ident_note(note):
+ note = StructParser(note)
+ (android_api,) = note.read_struct('<I', 'note descriptor')
+ print('ABI_ANDROID_API: {}'.format(android_api))
+ if note.empty:
+ return
+ # Binaries generated by NDK r14 and later have these extra fields. Platform
+ # binaries and binaries generated by older NDKs don't.
+ (ndk_version, ndk_build_number) = note.read_struct(
+ '{}s{}s'.format(NDK_RESERVED_SIZE, NDK_RESERVED_SIZE),
+ 'note descriptor')
+ print('ABI_NDK_VERSION: {}'.format(ndk_version.rstrip('\0')))
+ print('ABI_NDK_BUILD_NUMBER: {}'.format(ndk_build_number.rstrip('\0')))
+ if not note.empty:
+ logger().warning('excess data at end of descriptor')
# Get the offset to a section from the output of readelf
def get_section_pos(sec_name, file_path):
cmd = ['readelf', '--sections', '-W', file_path]
output = subprocess.check_output(cmd)
- lines = output.split('\n')
+ lines = output.splitlines()
for line in lines:
logger().debug('Checking line for "%s": %s', sec_name, line)
# Looking for a line like the following (all whitespace of unknown
@@ -58,29 +117,17 @@
# The only column that might have internal whitespace is the first one.
# Since we don't care about it, remove the head of the string until the
# closing bracket, then split.
- if sec_name not in line:
- continue
if ']' not in line:
continue
- line = line[line.index(']') + 1:].strip()
+ line = line[line.index(']') + 1:]
sections = line.split()
- if len(sections) < 5 or sec_name not in sections[0]:
- logger().debug('Did not find "%s" in %s', sec_name, sections[0])
- sys.exit('Failed to get offset of {}'.format(sec_name))
- addr = int(sections[2], 16)
+ if len(sections) < 5 or sec_name != sections[0]:
+ continue
off = int(sections[3], 16)
- return off
- sys.exit('Failed to find section: {}'.format(sec_name))
-
-
-def print_info(tag):
- print '----------ABI INFO----------'
- print 'ABI_NOTETYPE: {}'.format(tag.type)
- print 'ABI_VENDOR: {}'.format(tag.name)
- print 'ABI_ANDROID_API: {}'.format(tag.android_api)
- print 'ABI_NDK_VERSION: {}'.format(tag.ndk_version)
- print 'ABI_NDK_BUILD_NUMBER: {}'.format(tag.ndk_build_number)
+ size = int(sections[4], 16)
+ return (off, size)
+ sys.exit('error: failed to find section: {}'.format(sec_name))
def parse_args():
@@ -106,11 +153,22 @@
file_path = args.file_path
with open(file_path, "rb") as obj_file:
- pos = get_section_pos(SEC_NAME, file_path)
- obj_file.seek(pos)
- tag = AbiTag()
- obj_file.readinto(tag)
- print_info(tag)
+ (sec_off, sec_size) = get_section_pos(SEC_NAME, file_path)
+
+ obj_file.seek(sec_off)
+ sec_data = obj_file.read(sec_size)
+ if len(sec_data) != sec_size:
+ sys.exit('error: could not read {} section'.format(SEC_NAME))
+
+ print('----------ABI INFO----------')
+ if len(sec_data) == 0:
+ logger().warning('%s section is empty', SEC_NAME)
+ for (name, kind, desc) in iterate_notes(sec_data):
+ if (name, kind) == ('Android', 1):
+ dump_android_ident_note(desc)
+ else:
+ logger().warning('unrecognized note (name %s, type %d)',
+ repr(name), kind)
if __name__ == '__main__':