#!/usr/bin/env python
#   Copyright 2011 David Malcolm <dmalcolm@redhat.com>
#   Copyright 2011 Red Hat, Inc.
#
#   This is free software: you can redistribute it and/or modify it
#   under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful, but
#   WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#   General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see
#   <http://www.gnu.org/licenses/>.

# Harness for invoking GCC with the cpychecker Python code within the python
# plugin, whilst dealing with some options
# (This code runs under the regular Python interpreter, not within gcc)

import argparse
import os
import subprocess
import sys

abspath = os.path.abspath(os.path.dirname(sys.argv[0]))

# By default, look for the plugin relative to this harness.  This is intended
# to make it easier during development, so that we can make other projects with
#     CC=../../../gcc-python-plugin/gcc-with-cpychecker
# and similar.
#
# When installed, this should be fixed up.
PLUGIN = os.path.join(abspath, 'python.so')

# Similarly, set the location to search for libgcc-c-api.so:
LD_LIBRARY_PATH = os.path.join(abspath, 'gcc-c-api')
if 'LD_LIBRARY_PATH' in os.environ:
    LD_LIBRARY_PATH = '%s:%s' % (LD_LIBRARY_PATH, os.environ['LD_LIBRARY_PATH'])

# Create arg parser:
parser = argparse.ArgumentParser(usage='%(prog)s [options] gcc-options')
DEFAULT_MAXTRANS=256
parser.add_argument('--maxtrans',
                    type=int,
                    default=DEFAULT_MAXTRANS,
                    help='Set the maximum number of transitions to consider before pruning the analysis tree (default: %i)' % DEFAULT_MAXTRANS)

parser.add_argument('--dump-json',
                    action='store_true',
                    default=False,
                    help=('Dump a JSON representation of any problems.  For'
                          ' example, given a function "foo.c", if any warnings'
                          ' or errors are found in function "bar", a file'
                          ' "foo.c.bar.json" will be written out in JSON'
                          ' form'))

# Only consume args we understand, leaving the rest for gcc:
ns, other_args = parser.parse_known_args()
if 0:
    print(ns)
    print(other_args)

# Enable the refcount-checker when running via this script
#
# We would use the regular keyword argument syntax:
#   verify_refcounting=True
# but unfortunately gcc's option parser seems to not be able to cope with '='
# within an option's value.  So we do it using dictionary syntax instead:
dictstr = '"verify_refcounting":True'
dictstr += ', "maxtrans":%i' % ns.maxtrans
dictstr += ', "dump_json":%i' % ns.dump_json
cmd = 'from libcpychecker import main; main(**{%s})' % dictstr

# (Do not look up CC in the environment, to avoid forkbombing
# when setting CC=gcc-with-cpychecker)
args = ['gcc',
        ('-fplugin=%s' % PLUGIN),
        ('-fplugin-arg-python-command=%s' % cmd)]
args += other_args # (the args we didn't consume)

# Beware of quoting: if the command is quoted within the Popen call, then
# Python interprets it as a string literal, and does nothing.
#
# But if invoking from a shell, you need quotes aroung the command
#
# To add to the fun, "gcc -v" emits it in unquoted form,
# which will need quotes added

env = os.environ.copy()
env['LD_LIBRARY_PATH'] = LD_LIBRARY_PATH

if 0:
    print(' '.join(args))
p = subprocess.Popen(args, env=env)

try:
    r = p.wait()
except KeyboardInterrupt:
    r = 1
sys.exit(r)
