#!/usr/bin/env python3
#
# Copyright (C) 2020 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.
#

import argparse
import base64
import json
import logging
import os
import subprocess
import sys
import urllib.request

from abitool import get_abi_tool

_BASE_URL_ABI_XML = ('https://android.googlesource.com/kernel/common/'
                     '+/refs/heads/android-{}/abi_gki_aarch64.xml?format={}')

logger = logging.getLogger('gki_check')
logging.basicConfig(level=logging.INFO, format='%(message)s')


def main():
  parser = argparse.ArgumentParser()

  parser.add_argument(
      '--kernel-version',
      help='The Kernel version to compare against',
      choices=['4.19', '5.4'],
      default='5.4')

  parser.add_argument(
      '--linux-tree',
      help='Path to kernel tree containing '
      'vmlinux and modules',
      required=True)

  parser.add_argument(
      '--abi-tool',
      default='libabigail',
      help='tool to monitor the ABI')

  parser.add_argument(
      '--kmi-symbol-list', '--kmi-whitelist',
      default=None,
      help='KMI symbol list to filter for')

  args = parser.parse_args()

  abi_tool = get_abi_tool(args.abi_tool)

  # We assume the --linux-tree to be our working directory, hence download all
  # the stuff there.

  gki_check_dir = os.path.join(args.linux_tree, '.gki_check')
  logger.info('Creating working directory %s ...', gki_check_dir)
  os.makedirs(gki_check_dir, exist_ok=True)

  upstream_xml = os.path.join(gki_check_dir, 'upstream.xml')
  local_xml = os.path.join(gki_check_dir, 'local.xml')
  abi_report = os.path.join(gki_check_dir, 'abi.report')
  abi_report_short = os.path.join(gki_check_dir, 'abi.report.short')

  # Download GKI abi.xml from either 5.4 (default) or 4.19
  abi_xml_url = _BASE_URL_ABI_XML.format(args.kernel_version, 'TEXT')

  base_is_current = False
  if os.path.exists(upstream_xml):
    current_hash = subprocess.check_output(['git', 'hash-object',
                                            upstream_xml]).strip().decode()

    response = urllib.request.urlopen(
        _BASE_URL_ABI_XML.format(args.kernel_version, 'JSON'))
    data = response.read().decode('utf-8')
    new_hash = json.loads(data[data.index('{'):])['id']

    if current_hash == new_hash:
      base_is_current = True
      logger.info('ABI representation is already up-to-date. '
                  'Skipping download ...')

  if not base_is_current:
    logger.info('Downloading ABI representation for android-%s ...',
                args.kernel_version)

    response = urllib.request.urlopen(abi_xml_url)
    data = response.read()
    with open(upstream_xml, 'wb') as reference_xml:
      reference_xml.write(base64.b64decode(data))

  logger.info('Extracting ABI information from your local build ...')
  abi_tool.dump_kernel_abi(args.linux_tree, local_xml, args.kmi_symbol_list)

  logger.info('Comparing ABI information to reference ...')
  abi_tool.diff_abi(local_xml, upstream_xml, abi_report, abi_report_short,
                    args.kmi_symbol_list)

  logger.info('ABI Analysis complete, please find the result below')
  with open(abi_report_short) as short_report:
    print(short_report.read())

  logger.info('Upstream %s', upstream_xml)
  logger.info('Local representation: %s', local_xml)
  logger.info('Full report: %s', abi_report)
  logger.info('Short report: %s', abi_report_short)


if __name__ == '__main__':
  sys.exit(main())
