<commit_before>#! /usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import (unicode_literals, absolute_import, division,
                        print_function)

import tempfile
import plistlib
import argparse
import re
from os import path

from subprocess import check_output, check_call

# These settings are used for the 'strict' defaults style.
# All other settings values not explicitely defined here are inherited from
# the 'aggressive' style.
# The goal is to get as strict as possible, without making day-to-day
# development with these warnings a nightmare. This is intended to be
# community driven, do not hesitate to submit a PR if you feel one setting
# should have a stricter or looser default value.
#
# See https://pewpewthespells.com/blog/buildsettings.html for a description
# of each setting effect.
STRICT_DEFAULTS_EXCEPTIONS = {
    # https://openradar.appspot.com/radar?id=5907704967069696
    'CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK': 'NO',

    # It's sometime useful to test a few things in debug build
    # Maybe use `YES` for release builds
    'GCC_TREAT_WARNINGS_AS_ERRORS': 'NO',

    # Doesn't play nice with Apple Frameworks
    'GCC_WARN_PEDANTIC': 'NO',

    # Don't need that with Objective-C 2.0
    'CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS': 'NO',

    # Probably a bit controversial, but not uncommon to ignore some parameters
    # in Apple delegates methods
    'GCC_WARN_UNUSED_PARAMETER': 'NO',
}

###############################################################################

# Those are settings where 'YES' is not the most aggressive value
AGGRESSIVE_DEFAULTS_EXCEPTIONS = {
    'GCC_WARN_INHIBIT_ALL_WARNINGS': 'NO'
}


class XcspecOptionsGroup(object):
    def __init__(self, tool_name, group_name):
        super(XcspecOptionsGroup, self).__init__()
        self.tool_name = tool_name
        self.group_name = group_name
        self.options = []

    @property
    def display_name(self):
        display_names = {
            'WarningsObjCARC': 'Warnings - Objective C and ARC',
            'WarningsPolicy': 'Warnings - Warning Policies',
            'WarningsCXX': 'Warnings - C++',
            'WarningsObjC': 'Warnings - Objective C',
            'Warnings': 'Warnings - All languages',

            'SAObjCCheckers': 'Issues - Objective C',
            'SASecurityCheckers': 'Issues - Security',
            'SACheckers': 'Generic Issues',
            'SAPolicy': 'Analysis Policiy',
        }

        name = self.tool_name + ' - '
        if self.group_name in display_names:
            name += display_names[self.group_name]
        else:
            name += self.group_name

        return name

    def format_as_xcconfig(self, default_values=None, add_doc=False):
        s = '// {}\n'.format(self.display_name)

        for option in self.options:
            s += option.format_as_xcconfig(default_values=default_values, add_doc=add_doc)

        return s


class XcspecOption(object):
    def __init__(self, xcspec_dict):
        super(XcspecOption, self).__init__()
        self.from_xspec_dict(xcspec_dict)
        self.xcode_default_value = None
        self.strict_default_value = None

    def from_xspec_dict(self, xcspec_dict):
        self.name = xcspec_dict['Name']
        self.display_name = xcspec_dict.get('DisplayName')
        self.description = xcspec_dict.get('Description')
        self.type = xcspec_dict['Type']
        self.values = xcspec_dict.get('Values')
        self.clang_default_value = xcspec_dict.get('DefaultValue')

    @property
    def aggressive_default_value(self):
        if self.name in AGGRESSIVE_DEFAULTS_EXCEPTIONS:
            return AGGRESSIVE_DEFAULTS_EXCEPTIONS[self.name]

        if self.type == 'Boolean':
            return 'YES'
        elif self.type == 'Enumeration':
            if self.values == ['YES', 'NO']:
                return 'YES'
            elif 'YES_AGGRESSIVE' in self.values:
                return 'YES_AGGRESSIVE'
            elif 'YES_ERROR' in self.values:
                return 'YES_ERROR'
            elif set(self.values) == set(['shallow', 'deep']):
                return 'deep'
        raise Exception('Unknown aggressive value for {} ({})'
                        .format(self.name, self.type))

    def _default_value_for_style(self, style):
        if style == 'clang':
            value = self.clang_default_value

        elif style == 'xcode':
            value = self.xcode_default_value
            if value is None:
                value = self.clang_default_value

        elif style == 'strict':
            if self.name in STRICT_DEFAULTS_EXCEPTIONS:
                value = STRICT_DEFAULTS_EXCEPTIONS[self.name]
            else:
                value = self.aggressive_default_value

        elif style == 'aggressive':
            value = self.aggressive_default_value

        else:
            if self.type == 'Boolean':
                value = 'YES | NO'
            elif self.type == 'Enumeration':
                value = ' | '.join(self.values)
            else:
                value = self.type

            value = '// {}'.format(value)

        return value

    def format_as_xcconfig(self, default_values=None, add_doc=False):
        value = self._default_value_for_style(default_values)

        opt_str = ''

        if add_doc and self.description:
            opt_str += '// {}: {}\n'.format(self.display_name, self.description)

        opt_str += '{} = {}\n'.format(self.name, value)

        return opt_str


class XSpecParser(object):
    def __init__(self, filepath):
        super(XSpecParser, self).__init__()
        self._open_xcspec(filepath)

    def __exit__(self, exc_type, exc_value, traceback):
        self._xcspec_file.close()

    def _open_xcspec(self, xcspec_path):
        self._xcspec_file = tempfile.NamedTemporaryFile()

        # Apple uses the old NeXTSTEP format for its xcspec, convert it to
        # xml with plutil
        check_call(['plutil', '-convert', 'xml1', xcspec_path, '-o',
                    self._xcspec_file.name])

        self.xcspec_root = plistlib.readPlist(self._xcspec_file)

    def _get_tool_with_id(self, tool_identifier):
        xcspec_tool = None
        for tool in self.xcspec_root:
            if tool['Identifier'] == tool_identifier:
                xcspec_tool = tool

        if not xcspec_tool:
            raise Exception('Found no tool with identifier {} in '
                            'xcspec'.format(tool_identifier))

        # Inherit properties of the 'BasedOn' tool, if needed
        if 'BasedOn' in xcspec_tool:
            based_on_tool = self._get_tool_with_id(xcspec_tool['BasedOn'])
            full_tool = based_on_tool.copy()
            full_tool.update(xcspec_tool)
            xcspec_tool = full_tool

        return xcspec_tool

    def parse_options(self, tool_identifier, category_filter=None):
        if category_filter:
            category_filter = re.compile(category_filter)

        xcspec_tool = self._get_tool_with_id(tool_identifier)
        xcspec_tool_name = xcspec_tool['Name']
        xcspec_options = xcspec_tool['Options']

        options_groups = {}
        for xcspec_option in xcspec_options:
            if 'Category' not in xcspec_option:
                continue

            category = xcspec_option['Category']

            if category_filter and not category_filter.search(category):
                continue

            if category not in options_groups:
                options_groups[category] = XcspecOptionsGroup(xcspec_tool_name,
                                                              category)

            option = XcspecOption(xcspec_option)
            options_groups[category].options.append(option)

        return options_groups.values()


def import_xcode_defaults_into_options(xcode_path, options_groups):
    XCODE_REL_PROJECT_TEMPLATE_INFO_PATH = 'Contents/Developer/Library/Xcode/Templates/Project Templates/Base/Base.xctemplate/TemplateInfo.plist'
    xcode_template_path = path.join(xcode_path,
                                    XCODE_REL_PROJECT_TEMPLATE_INFO_PATH)
    xcode_template_info = plistlib.readPlist(xcode_template_path)
    xcode_defaults = xcode_template_info['Project']['SharedSettings']

    for options_group in options_groups:
        for option in options_group.options:<commit_msg>Fix Xcode Project Template plist location for Xcode 8<commit_after>#! /usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import (unicode_literals, absolute_import, division,
                        print_function)

import tempfile
import plistlib
import argparse
import re
from os import path

from subprocess import check_output, check_call

# These settings are used for the 'strict' defaults style.
# All other settings values not explicitely defined here are inherited from
# the 'aggressive' style.
# The goal is to get as strict as possible, without making day-to-day
# development with these warnings a nightmare. This is intended to be
# community driven, do not hesitate to submit a PR if you feel one setting
# should have a stricter or looser default value.
#
# See https://pewpewthespells.com/blog/buildsettings.html for a description
# of each setting effect.
STRICT_DEFAULTS_EXCEPTIONS = {
    # https://openradar.appspot.com/radar?id=5907704967069696
    'CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK': 'NO',

    # It's sometime useful to test a few things in debug build
    # Maybe use `YES` for release builds
    'GCC_TREAT_WARNINGS_AS_ERRORS': 'NO',

    # Doesn't play nice with Apple Frameworks
    'GCC_WARN_PEDANTIC': 'NO',

    # Don't need that with Objective-C 2.0
    'CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS': 'NO',

    # Probably a bit controversial, but not uncommon to ignore some parameters
    # in Apple delegates methods
    'GCC_WARN_UNUSED_PARAMETER': 'NO',
}

###############################################################################

# Those are settings where 'YES' is not the most aggressive value
AGGRESSIVE_DEFAULTS_EXCEPTIONS = {
    'GCC_WARN_INHIBIT_ALL_WARNINGS': 'NO'
}


class XcspecOptionsGroup(object):
    def __init__(self, tool_name, group_name):
        super(XcspecOptionsGroup, self).__init__()
        self.tool_name = tool_name
        self.group_name = group_name
        self.options = []

    @property
    def display_name(self):
        display_names = {
            'WarningsObjCARC': 'Warnings - Objective C and ARC',
            'WarningsPolicy': 'Warnings - Warning Policies',
            'WarningsCXX': 'Warnings - C++',
            'WarningsObjC': 'Warnings - Objective C',
            'Warnings': 'Warnings - All languages',

            'SAObjCCheckers': 'Issues - Objective C',
            'SASecurityCheckers': 'Issues - Security',
            'SACheckers': 'Generic Issues',
            'SAPolicy': 'Analysis Policiy',
        }

        name = self.tool_name + ' - '
        if self.group_name in display_names:
            name += display_names[self.group_name]
        else:
            name += self.group_name

        return name

    def format_as_xcconfig(self, default_values=None, add_doc=False):
        s = '// {}\n'.format(self.display_name)

        for option in self.options:
            s += option.format_as_xcconfig(default_values=default_values, add_doc=add_doc)

        return s


class XcspecOption(object):
    def __init__(self, xcspec_dict):
        super(XcspecOption, self).__init__()
        self.from_xspec_dict(xcspec_dict)
        self.xcode_default_value = None
        self.strict_default_value = None

    def from_xspec_dict(self, xcspec_dict):
        self.name = xcspec_dict['Name']
        self.display_name = xcspec_dict.get('DisplayName')
        self.description = xcspec_dict.get('Description')
        self.type = xcspec_dict['Type']
        self.values = xcspec_dict.get('Values')
        self.clang_default_value = xcspec_dict.get('DefaultValue')

    @property
    def aggressive_default_value(self):
        if self.name in AGGRESSIVE_DEFAULTS_EXCEPTIONS:
            return AGGRESSIVE_DEFAULTS_EXCEPTIONS[self.name]

        if self.type == 'Boolean':
            return 'YES'
        elif self.type == 'Enumeration':
            if self.values == ['YES', 'NO']:
                return 'YES'
            elif 'YES_AGGRESSIVE' in self.values:
                return 'YES_AGGRESSIVE'
            elif 'YES_ERROR' in self.values:
                return 'YES_ERROR'
            elif set(self.values) == set(['shallow', 'deep']):
                return 'deep'
        raise Exception('Unknown aggressive value for {} ({})'
                        .format(self.name, self.type))

    def _default_value_for_style(self, style):
        if style == 'clang':
            value = self.clang_default_value

        elif style == 'xcode':
            value = self.xcode_default_value
            if value is None:
                value = self.clang_default_value

        elif style == 'strict':
            if self.name in STRICT_DEFAULTS_EXCEPTIONS:
                value = STRICT_DEFAULTS_EXCEPTIONS[self.name]
            else:
                value = self.aggressive_default_value

        elif style == 'aggressive':
            value = self.aggressive_default_value

        else:
            if self.type == 'Boolean':
                value = 'YES | NO'
            elif self.type == 'Enumeration':
                value = ' | '.join(self.values)
            else:
                value = self.type

            value = '// {}'.format(value)

        return value

    def format_as_xcconfig(self, default_values=None, add_doc=False):
        value = self._default_value_for_style(default_values)

        opt_str = ''

        if add_doc and self.description:
            opt_str += '// {}: {}\n'.format(self.display_name, self.description)

        opt_str += '{} = {}\n'.format(self.name, value)

        return opt_str


class XSpecParser(object):
    def __init__(self, filepath):
        super(XSpecParser, self).__init__()
        self._open_xcspec(filepath)

    def __exit__(self, exc_type, exc_value, traceback):
        self._xcspec_file.close()

    def _open_xcspec(self, xcspec_path):
        self._xcspec_file = tempfile.NamedTemporaryFile()

        # Apple uses the old NeXTSTEP format for its xcspec, convert it to
        # xml with plutil
        check_call(['plutil', '-convert', 'xml1', xcspec_path, '-o',
                    self._xcspec_file.name])

        self.xcspec_root = plistlib.readPlist(self._xcspec_file)

    def _get_tool_with_id(self, tool_identifier):
        xcspec_tool = None
        for tool in self.xcspec_root:
            if tool['Identifier'] == tool_identifier:
                xcspec_tool = tool

        if not xcspec_tool:
            raise Exception('Found no tool with identifier {} in '
                            'xcspec'.format(tool_identifier))

        # Inherit properties of the 'BasedOn' tool, if needed
        if 'BasedOn' in xcspec_tool:
            based_on_tool = self._get_tool_with_id(xcspec_tool['BasedOn'])
            full_tool = based_on_tool.copy()
            full_tool.update(xcspec_tool)
            xcspec_tool = full_tool

        return xcspec_tool

    def parse_options(self, tool_identifier, category_filter=None):
        if category_filter:
            category_filter = re.compile(category_filter)

        xcspec_tool = self._get_tool_with_id(tool_identifier)
        xcspec_tool_name = xcspec_tool['Name']
        xcspec_options = xcspec_tool['Options']

        options_groups = {}
        for xcspec_option in xcspec_options:
            if 'Category' not in xcspec_option:
                continue

            category = xcspec_option['Category']

            if category_filter and not category_filter.search(category):
                continue

            if category not in options_groups:
                options_groups[category] = XcspecOptionsGroup(xcspec_tool_name,
                                                              category)

            option = XcspecOption(xcspec_option)
            options_groups[category].options.append(option)

        return options_groups.values()


def import_xcode_defaults_into_options(xcode_path, options_groups):
    XCODE_REL_PROJECT_TEMPLATE_INFO_PATH = 'Contents/Developer/Library/Xcode/Templates/Project Templates/Base/Base_ProjectSettings.xctemplate/TemplateInfo.plist'
    xcode_template_path = path.join(xcode_path,
                                    XCODE_REL_PROJECT_TEMPLATE_INFO_PATH)
    xcode_template_info = plistlib.readPlist(xcode_template_path)
    xcode_defaults = xcode_template_info['Project']['SharedSettings']

    for options_group in options_groups:
        for option in options_group.options:
{'fraction_non_alphanumeric': 0.09847036328871893, 'fraction_numerical': 0.00244980879541109, 'mean_word_length': 2.4673710379117466, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6113052', 'n_tokens_mistral': 5090, 'n_tokens_neox': 4840, 'n_words': 1125}