Make use of meta/platforms.json in ndk-build.

Test: nose2 build
Test: ./checkbuild.py && ./run_tests.py
Bug: None
Change-Id: Ie97f3fae5a9cafcd30c223d312bc29d24cd1d2c2
diff --git a/build/core/init.mk b/build/core/init.mk
index 827cc46..11736b9 100644
--- a/build/core/init.mk
+++ b/build/core/init.mk
@@ -449,23 +449,6 @@
   $(eval include $(BUILD_SYSTEM)/add-platform.mk)\
 )
 
-# we're going to find the maximum platform number of the form android-<number>
-# ignore others, which could correspond to special and experimental cases
-NDK_ALL_PLATFORM_LEVELS := $(filter android-%,$(NDK_ALL_PLATFORMS))
-NDK_ALL_PLATFORM_LEVELS := $(patsubst android-%,%,$(NDK_ALL_PLATFORM_LEVELS))
-$(call ndk_log,Found stable platform levels: $(NDK_ALL_PLATFORM_LEVELS))
-
-NDK_MIN_PLATFORM_LEVEL := 16
-NDK_MIN_PLATFORM := android-$(NDK_MIN_PLATFORM_LEVEL)
-
-NDK_MAX_PLATFORM_LEVEL := 3
-$(foreach level,$(NDK_ALL_PLATFORM_LEVELS),\
-  $(eval NDK_MAX_PLATFORM_LEVEL := $$(call max,$$(NDK_MAX_PLATFORM_LEVEL),$$(level)))\
-)
-NDK_MAX_PLATFORM := android-$(NDK_MAX_PLATFORM_LEVEL)
-
-$(call ndk_log,Found max platform level: $(NDK_MAX_PLATFORM_LEVEL))
-
 # Allow the user to point at an alternate location for the toolchains. This is
 # particularly helpful if we want to use prebuilt toolchains for building an NDK
 # module. Specifically, we use this to build libc++ using ndk-build instead of
@@ -494,13 +477,25 @@
 # the build script to include in each toolchain config.mk
 ADD_TOOLCHAIN := $(BUILD_SYSTEM)/add-toolchain.mk
 
-# ABI information is kept in meta/abis.json so it can be shared among multiple
-# build systems. Use Python to convert the JSON into make, replace the newlines
-# as necessary (make helpfully turns newlines into spaces for us...
-# https://www.gnu.org/software/make/manual/html_node/Shell-Function.html) and
-# eval the result.
-$(eval $(subst %NEWLINE%,$(newline),$(shell $(HOST_PYTHON) \
-    $(BUILD_PY)/import_abi_metadata.py $(NDK_ROOT)/meta/abis.json)))
+# Invokes a Python script that is expected to return make code and evaluates it.
+#
+# The Python script itself must use the string '%NEWLINE%' in place of actual
+# newlines because make helpfully turns newlines from $(shell) into spaces for
+# us (https://www.gnu.org/software/make/manual/html_node/Shell-Function.html).
+#
+# Args:
+#     1: Path to the script to be executed.
+#     2: Argument list.
+eval_python = \
+    $(eval $(subst %NEWLINE%,$(newline),$(shell $(HOST_PYTHON) $1 $2)))
+
+# NDK configuration metadata in JSON files in $NDK/meta so it can be shared
+# among multiple build systems. Delegate to Python rather than write a JSON
+# parser in make...
+$(call eval_python,\
+    $(BUILD_PY)/import_abi_metadata.py,$(NDK_ROOT)/meta/abis.json)
+$(call eval_python,\
+    $(BUILD_PY)/import_platforms_metadata.py,$(NDK_ROOT)/meta/platforms.json)
 
 NDK_KNOWN_DEVICE_ABIS := $(NDK_KNOWN_DEVICE_ABI64S) $(NDK_KNOWN_DEVICE_ABI32S)
 
@@ -508,6 +503,11 @@
 NDK_APP_ABI_ALL32_EXPANDED := $(NDK_KNOWN_DEVICE_ABI32S)
 NDK_APP_ABI_ALL64_EXPANDED := $(NDK_KNOWN_DEVICE_ABI64S)
 
+NDK_MIN_PLATFORM := android-$(NDK_MIN_PLATFORM_LEVEL)
+NDK_MAX_PLATFORM := android-$(NDK_MAX_PLATFORM_LEVEL)
+
+$(call ndk_log,Found max platform level: $(NDK_MAX_PLATFORM_LEVEL))
+
 # the list of all toolchains in this NDK
 NDK_ALL_TOOLCHAINS :=
 NDK_ALL_ABIS       :=
diff --git a/build/core/setup-app-platform.mk b/build/core/setup-app-platform.mk
index 9a1fa06..9326f97 100644
--- a/build/core/setup-app-platform.mk
+++ b/build/core/setup-app-platform.mk
@@ -53,52 +53,22 @@
     endif
 endif
 
+endif # APP_PROJECT_PATH == null
+
 ifeq ($(APP_PLATFORM),latest)
     $(call __ndk_info,Using latest available APP_PLATFORM: $(NDK_MAX_PLATFORM).)
     override APP_PLATFORM := $(NDK_MAX_PLATFORM)
 endif
 
-# Handle any platform codenames.
-ifeq ($(APP_PLATFORM),android-I)
-    override APP_PLATFORM := android-14
-else ifeq ($(APP_PLATFORM),android-J)
-    override APP_PLATFORM := android-16
-else ifeq ($(APP_PLATFORM),android-J-MR1)
-    override APP_PLATFORM := android-17
-else ifeq ($(APP_PLATFORM),android-J-MR2)
-    override APP_PLATFORM := android-18
-else ifeq ($(APP_PLATFORM),android-K)
-    override APP_PLATFORM := android-19
-else ifeq ($(APP_PLATFORM),android-L)
-    override APP_PLATFORM := android-21
-else ifeq ($(APP_PLATFORM),android-L-MR1)
-    override APP_PLATFORM := android-22
-else ifeq ($(APP_PLATFORM),android-M)
-    override APP_PLATFORM := android-23
-else ifeq ($(APP_PLATFORM),android-N)
-    override APP_PLATFORM := android-24
-else ifeq ($(APP_PLATFORM),android-N-MR1)
-    override APP_PLATFORM := android-25
-else ifeq ($(APP_PLATFORM),android-O)
-    override APP_PLATFORM := android-26
-else ifeq ($(APP_PLATFORM),android-O-MR1)
-    override APP_PLATFORM := android-27
-else ifeq ($(APP_PLATFORM),android-P)
-    override APP_PLATFORM := android-28
-endif
-
-endif # APP_PROJECT_PATH == null
-
-# Though the platform emits library directories for every API level, the
-# deprecated headers have gaps, and we only end up shipping libraries that match
-# a directory for which we actually have deprecated headers. We'll need to fix
-# this soon since we'll be adding O APIs to only the new headers, but for now
-# just preserve the old behavior.
+# Aliases defined by meta/platforms.json include codename aliases for platform
+# API levels as well as cover any gaps in platforms that may not have had NDK
+# APIs.
 APP_PLATFORM_LEVEL := $(strip $(subst android-,,$(APP_PLATFORM)))
-ifneq (,$(filter 20,$(APP_PLATFORM_LEVEL)))
-    override APP_PLATFORM := android-19
-else ifneq (,$(filter 25,$(APP_PLATFORM_LEVEL)))
-    override APP_PLATFORM := android-24
+ifdef NDK_PLATFORM_ALIAS_$(APP_PLATFORM_LEVEL)
+    _alias_target := $(NDK_PLATFORM_ALIAS_$(APP_PLATFORM_LEVEL))
+    $(call __ndk_info,$(APP_PLATFORM) is an alias for $(_alias_target). \
+        Adjusting APP_PLATFORM to match.)
+    override APP_PLATFORM := $(_alias_target)
 endif
 
 # For APP_PLATFORM values set below the minimum supported version, we could
diff --git a/build/import_abi_metadata.py b/build/import_abi_metadata.py
index 61fdffe..7ca4e30 100644
--- a/build/import_abi_metadata.py
+++ b/build/import_abi_metadata.py
@@ -17,33 +17,7 @@
 """Generates Make-importable code from meta/abis.json."""
 from __future__ import print_function
 
-import argparse
-import json
-import os
-
-
-NEWLINE = '%NEWLINE%'
-
-
-def parse_args():
-    parser = argparse.ArgumentParser()
-
-    parser.add_argument(
-        'abis_file', metavar='ABIS_FILE', type=os.path.abspath,
-        help='Path to the abis.json file.')
-
-    return parser.parse_args()
-
-
-def generate_make_vars(abi_vars):
-    lines = []
-    for var, value in abi_vars.items():
-        lines.append('{} := {}'.format(var, value))
-    # https://www.gnu.org/software/make/manual/html_node/Shell-Function.html
-    # Make's $(shell) function replaces real newlines with spaces. Use
-    # something we can easily identify that's unlikely to appear in a variable
-    # so we can replace it in make.
-    return NEWLINE.join(lines)
+import make  # pylint: disable=relative-import
 
 
 def metadata_to_make_vars(meta):
@@ -77,14 +51,5 @@
     return abi_vars
 
 
-def main():
-    args = parse_args()
-    with open(args.abis_file) as abis_file:
-        abis = json.load(abis_file)
-
-    abi_vars = metadata_to_make_vars(abis)
-    print(generate_make_vars(abi_vars))
-
-
 if __name__ == '__main__':
-    main()
+    make.metadata_to_make(metadata_to_make_vars)
diff --git a/build/import_platforms_metadata.py b/build/import_platforms_metadata.py
new file mode 100644
index 0000000..2c2aa85
--- /dev/null
+++ b/build/import_platforms_metadata.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2018 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 Make-importable code from meta/platforms.json."""
+from __future__ import print_function
+
+import make  # pylint: disable=relative-import
+
+
+def metadata_to_make_vars(meta):
+    min_api = meta['min']
+    max_api = meta['max']
+
+    make_vars = {
+        'NDK_MIN_PLATFORM_LEVEL': min_api,
+        'NDK_MAX_PLATFORM_LEVEL': max_api,
+    }
+
+    for src, dst in meta['aliases'].items():
+        name = 'NDK_PLATFORM_ALIAS_{}'.format(src)
+        value = 'android-{}'.format(dst)
+        make_vars[name] = value
+
+    return make_vars
+
+
+if __name__ == '__main__':
+    make.metadata_to_make(metadata_to_make_vars)
diff --git a/build/make.py b/build/make.py
new file mode 100644
index 0000000..017d946
--- /dev/null
+++ b/build/make.py
@@ -0,0 +1,51 @@
+#
+# Copyright (C) 2018 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.
+#
+"""APIs for interfacing with make."""
+from __future__ import absolute_import
+from __future__ import print_function
+
+import argparse
+import json
+import os
+
+
+NEWLINE = '%NEWLINE%'
+
+
+def generate_make_vars(abi_vars):
+    lines = []
+    for var, value in abi_vars.items():
+        lines.append('{} := {}'.format(var, value))
+    # https://www.gnu.org/software/make/manual/html_node/Shell-Function.html
+    # Make's $(shell) function replaces real newlines with spaces. Use
+    # something we can easily identify that's unlikely to appear in a variable
+    # so we can replace it in make.
+    return NEWLINE.join(lines)
+
+
+def metadata_to_make(conversion_func):
+    parser = argparse.ArgumentParser()
+
+    parser.add_argument(
+        'metadata_file', metavar='METADATA_FILE', type=os.path.abspath,
+        help='Path to the metadata JSON file.')
+
+    args = parser.parse_args()
+    with open(args.metadata_file) as metadata_file:
+        metadata = json.load(metadata_file)
+
+    metadata_vars = conversion_func(metadata)
+    print(generate_make_vars(metadata_vars))
diff --git a/build/test_import_abi_metadata.py b/build/test_import_abi_metadata.py
index f82b0cb..b33b203 100644
--- a/build/test_import_abi_metadata.py
+++ b/build/test_import_abi_metadata.py
@@ -23,17 +23,6 @@
 
 
 class ImportAbiMetadataTest(unittest.TestCase):
-    def test_generate_make_vars(self):
-        self.assertEqual(
-            'foo := bar',
-            build.import_abi_metadata.generate_make_vars(
-                {'foo': 'bar'}))
-        self.assertEqual(
-            build.import_abi_metadata.NEWLINE.join(
-                ['foo := bar', 'baz := qux']),
-            build.import_abi_metadata.generate_make_vars(
-                {'foo': 'bar', 'baz': 'qux'}))
-
     def test_metadata_to_make_vars(self):
         make_vars = build.import_abi_metadata.metadata_to_make_vars({
             'armeabi': {
diff --git a/build/test_import_platforms_metadata.py b/build/test_import_platforms_metadata.py
new file mode 100644
index 0000000..2c37b36
--- /dev/null
+++ b/build/test_import_platforms_metadata.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2017 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.
+#
+"""Tests for import_abi_metadata.py."""
+from __future__ import print_function
+
+import unittest
+
+import build.import_platforms_metadata
+
+
+class ImportPlatformsMetadataTest(unittest.TestCase):
+    def test_metadata_to_make_vars(self):
+        make_vars = build.import_platforms_metadata.metadata_to_make_vars({
+            'min': 16,
+            'max': 28,
+            'aliases': {
+                '20': 19,
+                'J': 16,
+                'O': 26,
+            },
+        })
+
+        self.assertDictEqual({
+            'NDK_MIN_PLATFORM_LEVEL': 16,
+            'NDK_MAX_PLATFORM_LEVEL': 28,
+            'NDK_PLATFORM_ALIAS_20': 'android-19',
+            'NDK_PLATFORM_ALIAS_J': 'android-16',
+            'NDK_PLATFORM_ALIAS_O': 'android-26',
+        }, make_vars)
diff --git a/build/test_make.py b/build/test_make.py
new file mode 100644
index 0000000..59bdc64
--- /dev/null
+++ b/build/test_make.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2017 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.
+#
+"""Tests for build.make."""
+from __future__ import print_function
+
+import unittest
+
+import build.make
+
+
+class MakeVarsTest(unittest.TestCase):
+    def test_generate_make_vars(self):
+        self.assertEqual(
+            'foo := bar',
+            build.make.generate_make_vars({'foo': 'bar'}))
+        self.assertEqual(
+            build.make.NEWLINE.join(['foo := bar', 'baz := qux']),
+            build.make.generate_make_vars({'foo': 'bar', 'baz': 'qux'}))