<commit_before># -*- coding: utf-8 -*-
#
# Script to convert Visual Studio (express) solution and project files
# from one version to another.
#
# Currently supports converting:
# * 2008 (10.0) to 2010 (11.0)
#
# Copyright (c) 2013, <NAME> <<EMAIL>>
#
# 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.

# TODO: vs2010 writer add 64-bit target.
# TODO: allow to generate from source tree, e.g. Makefile.am?
# TODO: add automated tests.
# TODO: complete vs2010 reader.
# TODO: complete vs2008 writer.

import abc
import argparse
import re
import os
import sys


class VSConfiguration(object):
  """Class to represent a Visual Studio configurations."""

  def __init__(self):
    """Initializes a Visual Studio configuration."""
    self.name = ''
    self.platform = ''

  @abc.abstractmethod
  def CopyToX64(self):
    """Copies the Visual Studio solution configuration to an x64 equivalent."""


class VSConfigurations(object):
  """Class to represent a Visual Studio solution and project configurations."""

  def __init__(self):
    """Initializes a Visual Studio configurations."""
    self._configurations = {}
    self.names = []
    self.platforms = []

  @property
  def number_of_configurations(self):
    return len(self._configurations.values())

  def Append(self, configuration):
    """Appends a configuration.

    Args:
      configuration: the configuration (instance of VSConfiguration).
    """
    if configuration.name not in self.names:
      self.names.append(configuration.name)

    if configuration.platform not in self.platforms:
      self.platforms.append(configuration.platform)

    identifier = '{0:s}|{1:s}'.format(
        configuration.name, configuration.platform)

    self._configurations[identifier] = configuration

  def ExtendWithX64(self):
    """Extends the configurations with the x64 platform."""
    if 'x64' not in self.platforms:
      for configuration in self._configurations.values():
        if configuration.platform != 'x64':
          self.Append(configuration.CopyToX64())

  def GetByIdentifier(self, name, platform):
    """Retrieves a specific configuration by identtifier.

    The identifier is formatted as: name|platform.

    Args:
      name: the configuration name.
      platform: the configuration platform.

    Returns:
      The configuration (instance of VSProjectConfiguration or
      VSProjectConfiguration).
    """
    identifier = '{0:s}|{1:s}'.format(name, platform)
    return self._configurations[identifier]

  def GetSorted(self, reverse=False):
    """Retrieves configurations in sorted order.

    The sorting order is first alphabetacally by name,
    secondly alphabetacally by platform.

    Args:
      reverse: optional boolean to indicate the name sort order should be
               reversed, which is False by default. The platform sort
               order does not change.

    Yields:
      The configuration (instance of VSConfiguration).
    """
    for name in sorted(self.names, reverse=reverse):
      for platform in sorted(self.platforms):
        yield self.GetByIdentifier(name, platform)


class VSProjectConfiguration(VSConfiguration):
  """Class to represent a Visual Studio project configuration."""

  def __init__(self):
    """Initializes a Visual Studio project configuration."""
    super(VSProjectConfiguration, self).__init__()

    # Note that name and platform are inherited from VSConfiguration.
    self.basic_runtime_checks = ''
    self.character_set = ''
    self.compile_as = ''
    self.data_execution_prevention = ''
    self.debug_information_format = ''
    self.dependencies = ''
    self.dependencies_set = False
    self.enable_comdat_folding = ''
    self.generate_debug_information = ''
    self.import_library = ''
    self.include_directories = ''
    self.librarian_ignore_defaults = ''
    self.librarian_output_file = ''
    self.library_directories = ''
    self.link_incremental = ''
    self.linker_output_directory = ''
    self.linker_output_file = ''
    self.linker_values_set = False
    self.optimize_references = ''
    self.optimization = ''
    self.output_type = ''
    self.platform_toolset = ''
    self.precompiled_header = ''
    self.preprocessor_definitions = ''
    self.randomized_base_address = ''
    self.runtime_library = ''
    self.smaller_type_check = ''
    self.sub_system = ''
    self.target_machine = ''
    self.warning_as_error = ''
    self.warning_level = ''
    self.whole_program_optimization = ''

  @property
  def basic_runtime_checks_string(self):
    basic_runtime_checks = int(self.basic_runtime_checks, 10)
    if basic_runtime_checks == 3:
      return 'EnableFastChecks'
    return ''

  @property
  def character_set_string(self):
    character_set = int(self.character_set, 10)
    if character_set == 1:
      return 'Unicode'
    return ''

  @property
  def compile_as_string(self):
    compile_as = int(self.compile_as, 10)
    if compile_as == 1:
      return 'CompileAsC'
    return ''

  @property
  def data_execution_prevention_string(self):
    data_execution_prevention = int(self.data_execution_prevention, 10)
    if data_execution_prevention == 2:
      return 'true'
    return ''

  @property
  def debug_information_format_string(self):
    debug_information_format = int(self.debug_information_format, 10)
    if debug_information_format == 3:
      return 'ProgramDatabase'
    return ''

  @property
  def enable_comdat_folding_string(self):
    enable_comdat_folding = int(self.enable_comdat_folding, 10)
    if enable_comdat_folding == 2:
      return 'true'
    return ''

  @property
  def link_incremental_string(self):
    link_incremental = int(self.link_incremental, 10)
    if link_incremental == 1:
      return 'false'
    return ''

  @property
  def optimize_references_string(self):
    optimize_references = int(self.optimize_references, 10)
    if optimize_references == 2:
      return 'true'
    return ''

  @property
  def optimization_string(self):
    optimization = int(self.optimization, 10)
    if optimization == 0:
      return 'Disabled'
    return ''

  @property
  def output_type_string(self):
    output_type = int(self.output_type, 10)
    if output_type == 1:
      return 'Application'
    elif output_type == 2:
      return 'DynamicLibrary'
    elif output_type == 4:
      return 'StaticLibrary'
    return ''

  @property
  def precompiled_header_string(self):
    precompiled_header = int(self.precompiled_header, 10)
    # TODO: do something with precompiled_header.
    return ''

  @property
  def randomized_base_address_string(self):
    randomized_base_address = int(self.randomized_base_address, 10)
    if randomized_base_address == 1:
      return 'false'
    elif randomized_base_address == 2:
      return 'true'
    return ''

  @property
  def runtime_librarian_string(self):
    runtime_library = int(self.runtime_library, 10)
    if runtime_library == 2:
      return 'MultiThreadedDLL'
    if runtime_library == 3:
      return 'MultiThreadedDebugDLL'
    return ''

  @property
  def sub_system_string(self):
    sub_system = int(self.sub_system, 10)
    if sub_system == 1:
      return 'Console'
    return ''

  @property
  def target_machine_string(self):
    target_machine = int(self.target_machine, 10)
    if target_machine == 1:
      return 'MachineX86'
    # TODO: assuming here that 2 is x64.
    elif target_machine == 2:
      return 'MachineX64'
    return ''

  @property
  def warning_level_string(self):
    warning_level = int(self.warning_level, 10)
    if warning_level == 4:
      return 'Level4'
    return ''

  @property
  def whole_program_optimization_string(self):
    whole_program_optimization = int(self.whole_program_optimization, 10)
    if whole_program_optimization == 1:
      return 'true'
    return ''

  def CopyToX64(self):
    """Copies the Visual Studio project configuration to an x64 equivalent."""
    copy = VSProjectConfiguration()

    copy.basic_runtime_checks = self.basic_runtime_checks
    copy.character_set = self.character_set
    copy.compile_as = self.compile_as
    copy.data_execution_prevention = self.data_execution_prevention
    copy.debug_information_format = self.debug_information_format
    copy.dependencies = self.dependencies
    copy.dependencies_set = self.dependencies_set
    copy.enable_comdat_folding = self.enable_comdat_folding
    copy.generate_debug_information = self.generate_debug_information
    copy.import_library = self.import_library
    copy.include_directories = self.include_directories
    copy.librarian_ignore_defaults = self.librarian_ignore_defaults
    copy.librarian_output_file = self.librarian_output_file
    copy.library_directories = self.library_directories
    copy.link_incremental = self.link_incremental
    copy.linker_output_directory = self.linker_output_directory
    copy.linker_output_file = self.linker_output_file
    copy.linker_values_set = self.linker_values_set
    copy.name = self.name
    copy.optimize_references = self.optimize_references
    copy.optimization = self.optimization
    copy.output_type = self.output_type
    copy.platform = 'x64'
    copy.platform_toolset = 'Windows7.1SDK'
    copy.precompiled_header = self.precompiled_header
    copy.preprocessor_definitions = self.preprocessor_definitions
    copy.randomized_base_address = self.randomized_base_address
    copy.runtime_library = self.runtime_library
    copy.smaller_type_check = self.smaller_type_check
    copy.sub_system = self.sub_system
    copy.target_machine = '2'
    copy.warning_as_error = self.warning_as_error
    copy.warning_level = self.warning_level
    copy.whole_program_optimization = self.whole_program_optimization

    return copy


class VSProjectInformation(object):
  """Class to represent a Visual Studio project information."""

  def __init__(self):
    """Initializes Visual Studio project information."""
    self.project_guid = ''
    self.root_name_space = ''
    self.keyword = ''


class VSSolutionConfiguration(VSConfiguration):
  """Class to represent a Visual Studio solution configuration."""

  def CopyToX64(self):
    """Copies the Visual Studio solution configuration to an x64 equivalent."""
    copy = VSSolutionConfiguration()

    copy.name = self.name
    copy.platform = 'x64'

    return copy


class VSSolutionProject(object):
  """Class to represent a Visual Studio solution project."""

  def __init__(self, name, filename, guid):
    """Initializes a Visual Studio project.

    Args:
      name: the project name.
      filename: the project filename without extension.
      guid: the project identifier (GUID).
    """
    self.name = name
    self.filename = filename
    self.guid = guid.lower()
    self.dependencies = []

  def AddDependency(self, dependency_guid):
    """Adds a dependency GUID to the project.

    Args:
      dependency_guid: the project GUID of the dependency.
    """
    self.dependencies.append(dependency_guid.lower())


class FileReader(object):
  """Class to represent a file reader."""

  def __init__(self):
    """Initializes a Visual Studio solution file reader."""
    self._line = None

  def Open(self, filename):
    """Opens the solution file.

    Args:
      filename: the filename of the solution file.
    """
    # For reading these files we don't care about the actual end of lines.
    self._file = open(filename, 'r')

  def Close(self):
    """Closes the solution file."""
    self._file.close()

  def _ReadLine(self, look_ahead=False):
    """Reads a line.

    Args:
      look_ahead: optional boolean value to indicate the line
                  should be considered read (False) or not (True).
                  The default is to consider the line read.

    Returns:
      The line stripped of leading and trailing white space
      or None if no input is available.
    """
    if self._line != None:
      line = self._line
      if not look_ahead:
        self._line = None

    else:
      line = self._file.readline()
      if line:
        line = line.strip()
      if look_ahead:
        self._line = line

    return line


class VSProjectFileReader(FileReader):
  """Class to represent a Visual Studio project file reader."""


class VS2008ProjectFileReader(VSProjectFileReader):
  """Class to represent a Visual Studio 2008 project file reader."""

  def ReadHeader(self):
    """Reads a file header.

    Returns:
      True if successful or false otherwise.
    """
    line = self._ReadLine()

    if not line:
      return False

    if not line.startswith('<?xml version="1.0"'):
      return False

    # TODO check encoding?

    line = self._ReadLine()

    if not line:
      return False

    if not line.startswith('<VisualStudioProject'):
      return False

    line = self._ReadLine()

    if not line:
      return False

    if not line.startswith('ProjectType="Visual C++"'):
      return False

    line = self._ReadLine()

    if not line:
      return False

    if not line.startswith('Version="9,00"'):
      return False

    return True

  def ReadProjectInformation(self):
    """Reads project information.

    Returns:
      Project information (instance of VSProjectInformation) if successful
      or None otherwise.
    """
    project_information = VSProjectInformation()

    line = self._ReadLine()
    while line:
      if line.startswith('>'):
        break

      elif line.startswith('ProjectGUID='):
        values = re.findall('ProjectGUID="([^"]*)"', line)
        if len(values) == 1:
          project_information.project_guid = values[0]

      elif line.startswith('RootNamespace='):
        values = re.findall('RootNamespace="([^"]*)"', line)
        if len(values) == 1:
          project_information.root_name_space = values[0]

      elif line.startswith('Keyword='):
        values = re.findall('Keyword="([^"]*)"', line)
        if len(values) == 1:
          project_information.keyword = values[0]

      line = self._ReadLine()

    return project_information

  def ReadConfigurations(self):
    """Reads the configurations.

    Returns:
      The configurations (instance of VSConfigurations).
    """
    configurations = VSConfigurations()

    # Find the start of the configurations section.
    result = False
    line = self._ReadLine()

    while line:
      result = line.startswith('<Configurations>')
      if result:
        break
      line = self._ReadLine()

    if result:
      while line:
        line = self._ReadLine()

        if line.startswith('</Configurations>'):
          break

        elif line.startswith('<Configuration'):
          configuration = self._ReadConfiguration(line)

          if configuration:
            configurations.Append(configuration)

    return configurations

  def _ReadConfiguration(self, line):
    """Reads a configuration.

    Args:
      line: the line that contains the start of the configuration section.

    Returns:
      A configuration (instance of VSProjectConfiguration) or None if
      no configuration was found.
    """
    if not line.startswith('<Configuration'):
      return None

    configuration = VSProjectConfiguration()

    found_tool = False
    found_tool_compiler = False
    found_tool_librarian = False
    found_tool_linker = False

    while line:
      line = self._ReadLine()

      if line.startswith('</Configuration>'):
        break

      elif found_tool:
        if line.startswith('/>'):
          found_tool = False
          found_tool_compiler = False
          found_tool_librarian = False
          found_tool_linker = False

        elif found_tool_compiler:
          # Parse the compiler specific configuration.
          if line.startswith('Optimization='):
            values = re.findall('Optimization="([^"]*)"', line)
            if len(values) == 1:
              configuration.optimization = values[0]

          elif line.startswith('AdditionalIncludeDirectories='):
            values = re.findall(
                'AdditionalIncludeDirectories="([^"]*)"', line)
            if len(values) == 1:
              configuration.include_directories = values[0]

          elif line.startswith('PreprocessorDefinitions='):
            values = re.findall(
                'PreprocessorDefinitions="([^"]*)"', line)
            if len(values) == 1:
              configuration.preprocessor_definitions = values[0]

          elif line.startswith('BasicRuntimeChecks='):
            values = re.findall('BasicRuntimeChecks="([^"]*)"', line)
            if len(values) == 1:
              configuration.basic_runtime_checks = values[0]

          elif line.startswith('SmallerTypeCheck='):
            values = re.findall('SmallerTypeCheck="([^"]*)"', line)
            if len(values) == 1:
              configuration.smaller_type_check = values[0]

          elif line.startswith('RuntimeLibrary='):
            values = re.findall('RuntimeLibrary="([^"]*)"', line)
            if len(values) == 1:
              configuration.runtime_library = values[0]

          elif line.startswith('UsePrecompiledHeader='):
            values = re.findall('UsePrecompiledHeader="([^"]*)"', line)
            if len(values) == 1:
              configuration.precompiled_header = values[0]

          elif line.startswith('WarningLevel='):
            values = re.findall('WarningLevel="([^"]*)"', line)
            if len(values) == 1:
              configuration.warning_level = values[0]

          elif line.startswith('WarnAsError='):
            values = re.findall('WarnAsError="([^"]*)"', line)
            if len(values) == 1:
              configuration.warning_as_error = values[0]

          elif line.startswith('DebugInformationFormat='):
            values = re.findall(
                'DebugInformationFormat="([^"]*)"', line)
            if len(values) == 1:
              configuration.debug_information_format = values[0]

          elif line.startswith('CompileAs='):
            values = re.findall('CompileAs="([^"]*)"', line)
            if len(values) == 1:
              configuration.compile_as = values[0]

        elif found_tool_librarian:
          # Parse the libararian specific configuration.
          if line.startswith('OutputFile='):
            values = re.findall('OutputFile="([^"]*)"', line)
            if len(values) == 1:
              configuration.librarian_output_file = values[0]

          elif line.startswith('IgnoreAllDefaultLibraries='):
            values = re.findall(
                'IgnoreAllDefaultLibraries="([^"]*)"', line)
            if len(values) == 1:
              configuration.librarian_ignore_defaults = values[0]

        elif found_tool_linker:
          # Parse the linker specific configuration.
          if line.startswith('OutputDirectory='):
            configuration.linker_values_set = True
            values = re.findall('OutputDirectory="([^"]*)"', line)
            if len(values) == 1:
              configuration.linker_output_directory = values[0]

          elif line.startswith('OutputFile='):
            configuration.linker_values_set = True
            values = re.findall('OutputFile="([^"]*)"', line)
            if len(values) == 1:
              configuration.linker_output_file = values[0]

          elif line.startswith('AdditionalDependencies='):
            configuration.linker_values_set = True
            values = re.findall(
                'AdditionalDependencies="([^"]*)"', line)
            if len(values) == 1:
              configuration.dependencies = values[0]
              configuration.dependencies_set = True

          elif line.startswith('LinkIncremental='):
            configuration.linker_values_set = True
            values = re.findall('LinkIncremental="([^"]*)"', line)
            if len(values) == 1:
              configuration.link_incremental = values[0]

          elif line.startswith('AdditionalLibraryDirectories='):
            configuration.linker_values_set = True
            values = re.findall(
                'AdditionalLibraryDirectories="([^"]*)"', line)
            if len(values) == 1:
              configuration.library_directories = values[0]

          elif line.startswith('GenerateDebugInformation='):
            configuration.linker_values_set = True
            values = re.findall(
                'GenerateDebugInformation="([^"]*)"', line)
            if len(values) == 1:
              configuration.generate_debug_information = values[0]

          elif line.startswith('SubSystem='):
            configuration.linker_values_set = True
            values = re.findall(
                'SubSystem="([^"]*)"', line)
            if len(values) == 1:
              configuration.sub_system = values[0]

          elif line.startswith('OptimizeReferences='):
            configuration.linker_values_set = True
            values = re.findall(
                'OptimizeReferences="([^"]*)"', line)
            if len(values) == 1:
              configuration.optimize_references = values[0]

          elif line.startswith('RandomizedBaseAddress='):
            configuration.linker_values_set = True
            values = re.findall(
                'RandomizedBaseAddress="([^"]*)"', line)
            if len(values) == 1:
              configuration.randomized_base_address = values[0]

          elif line.startswith('EnableCOMDATFolding='):
            configuration.linker_values_set = True
            values = re.findall(
                'EnableCOMDATFolding="([^"]*)"', line)
            if len(values) == 1:
              configuration.enable_comdat_folding = values[0]

          elif line.startswith('DataExecutionPrevention='):
            configuration.linker_values_set = True
            values = re.findall(
                'DataExecutionPrevention="([^"]*)"', line)
            if len(values) == 1:
              configuration.data_execution_prevention = values[0]

          elif line.startswith('ImportLibrary='):
            configuration.linker_values_set = True
            values = re.findall(
                'ImportLibrary="([^"]*)"', line)
            if len(values) == 1:
              configuration.import_library = values[0]

          elif line.startswith('TargetMachine='):
            configuration.linker_values_set = True
            values = re.findall('TargetMachine="([^"]*)"', line)
            if len(values) == 1:
              configuration.target_machine = values[0]

        elif line.startswith('Name="VCCLCompilerTool"'):
          found_tool_compiler = True

        elif line.startswith('Name="VCLibrarianTool"'):
          found_tool_librarian = True

        elif line.startswith('Name="VCLinkerTool"'):
          found_tool_linker = True

      elif line.startswith('<Tool'):
        found_tool = True

      elif line.startswith('Name='):
        # For more than 1 match findall will return a list with a tuple.
        values = re.findall('Name="([^|]*)[|]([^"]*)"', line)[0]
        if len(values) == 2:
          configuration.name = values[0]
          configuration.platform = values[1]

      elif line.startswith('ConfigurationType='):
        values = re.findall('ConfigurationType="([^"]*)"', line)
        if len(values) == 1:
          configuration.output_type = values[0]

      elif line.startswith('CharacterSet='):
        values = re.findall('CharacterSet="([^"]*)"', line)
        if len(values) == 1:
          configuration.character_set = values[0]

      elif line.startswith('WholeProgramOptimization='):
        values = re.findall('WholeProgramOptimization="([^"]*)"', line)
        if len(values) == 1:
          configuration.whole_program_optimization = values[0]

      # TODO: PlatformToolset.

    return configuration

  def ReadFiles(self):
    """Reads the files.

    Returns:<commit_msg>Worked on msvscpp-convert.py<commit_after># -*- coding: utf-8 -*-
#
# Script to convert Visual Studio (express) solution and project files
# from one version to another.
#
# Currently supports converting:
# * 2008 (10.0) to 2010 (11.0)
#
# Copyright (c) 2013, <NAME> <<EMAIL>>
#
# 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.

# TODO: allow to generate from source tree, e.g. Makefile.am?
# TODO: add automated tests.
# TODO: complete vs2010 reader.
# TODO: complete vs2008 writer.

import abc
import argparse
import re
import os
import sys


class VSConfiguration(object):
  """Class to represent a Visual Studio configurations."""

  def __init__(self):
    """Initializes a Visual Studio configuration."""
    self.name = ''
    self.platform = ''

  @abc.abstractmethod
  def CopyToX64(self):
    """Copies the Visual Studio solution configuration to an x64 equivalent."""


class VSConfigurations(object):
  """Class to represent a Visual Studio solution and project configurations."""

  def __init__(self):
    """Initializes a Visual Studio configurations."""
    self._configurations = {}
    self.names = []
    self.platforms = []

  @property
  def number_of_configurations(self):
    return len(self._configurations.values())

  def Append(self, configuration):
    """Appends a configuration.

    Args:
      configuration: the configuration (instance of VSConfiguration).
    """
    if configuration.name not in self.names:
      self.names.append(configuration.name)

    if configuration.platform not in self.platforms:
      self.platforms.append(configuration.platform)

    identifier = '{0:s}|{1:s}'.format(
        configuration.name, configuration.platform)

    self._configurations[identifier] = configuration

  def ExtendWithX64(self):
    """Extends the configurations with the x64 platform."""
    if 'x64' not in self.platforms:
      for configuration in self._configurations.values():
        if configuration.platform != 'x64':
          self.Append(configuration.CopyToX64())

  def GetByIdentifier(self, name, platform):
    """Retrieves a specific configuration by identtifier.

    The identifier is formatted as: name|platform.

    Args:
      name: the configuration name.
      platform: the configuration platform.

    Returns:
      The configuration (instance of VSProjectConfiguration or
      VSProjectConfiguration).
    """
    identifier = '{0:s}|{1:s}'.format(name, platform)
    return self._configurations[identifier]

  def GetSorted(self, reverse=False):
    """Retrieves configurations in sorted order.

    The sorting order is first alphabetacally by name,
    secondly alphabetacally by platform.

    Args:
      reverse: optional boolean to indicate the name sort order should be
               reversed, which is False by default. The platform sort
               order does not change.

    Yields:
      The configuration (instance of VSConfiguration).
    """
    for name in sorted(self.names, reverse=reverse):
      for platform in sorted(self.platforms):
        yield self.GetByIdentifier(name, platform)


class VSProjectConfiguration(VSConfiguration):
  """Class to represent a Visual Studio project configuration."""

  def __init__(self):
    """Initializes a Visual Studio project configuration."""
    super(VSProjectConfiguration, self).__init__()

    # Note that name and platform are inherited from VSConfiguration.
    self.basic_runtime_checks = ''
    self.character_set = ''
    self.compile_as = ''
    self.data_execution_prevention = ''
    self.debug_information_format = ''
    self.dependencies = ''
    self.dependencies_set = False
    self.enable_comdat_folding = ''
    self.generate_debug_information = ''
    self.import_library = ''
    self.include_directories = ''
    self.librarian_ignore_defaults = ''
    self.librarian_output_file = ''
    self.library_directories = ''
    self.link_incremental = ''
    self.linker_output_directory = ''
    self.linker_output_file = ''
    self.linker_values_set = False
    self.optimize_references = ''
    self.optimization = ''
    self.output_type = ''
    self.platform_toolset = ''
    self.precompiled_header = ''
    self.preprocessor_definitions = ''
    self.randomized_base_address = ''
    self.runtime_library = ''
    self.smaller_type_check = ''
    self.sub_system = ''
    self.target_machine = ''
    self.warning_as_error = ''
    self.warning_level = ''
    self.whole_program_optimization = ''

  @property
  def basic_runtime_checks_string(self):
    basic_runtime_checks = int(self.basic_runtime_checks, 10)
    if basic_runtime_checks == 3:
      return 'EnableFastChecks'
    return ''

  @property
  def character_set_string(self):
    character_set = int(self.character_set, 10)
    if character_set == 1:
      return 'Unicode'
    return ''

  @property
  def compile_as_string(self):
    compile_as = int(self.compile_as, 10)
    if compile_as == 1:
      return 'CompileAsC'
    return ''

  @property
  def data_execution_prevention_string(self):
    data_execution_prevention = int(self.data_execution_prevention, 10)
    if data_execution_prevention == 2:
      return 'true'
    return ''

  @property
  def debug_information_format_string(self):
    debug_information_format = int(self.debug_information_format, 10)
    if debug_information_format == 3:
      return 'ProgramDatabase'
    return ''

  @property
  def enable_comdat_folding_string(self):
    enable_comdat_folding = int(self.enable_comdat_folding, 10)
    if enable_comdat_folding == 2:
      return 'true'
    return ''

  @property
  def link_incremental_string(self):
    link_incremental = int(self.link_incremental, 10)
    if link_incremental == 1:
      return 'false'
    return ''

  @property
  def optimize_references_string(self):
    optimize_references = int(self.optimize_references, 10)
    if optimize_references == 2:
      return 'true'
    return ''

  @property
  def optimization_string(self):
    optimization = int(self.optimization, 10)
    if optimization == 0:
      return 'Disabled'
    return ''

  @property
  def output_type_string(self):
    output_type = int(self.output_type, 10)
    if output_type == 1:
      return 'Application'
    elif output_type == 2:
      return 'DynamicLibrary'
    elif output_type == 4:
      return 'StaticLibrary'
    return ''

  @property
  def precompiled_header_string(self):
    precompiled_header = int(self.precompiled_header, 10)
    # TODO: do something with precompiled_header.
    return ''

  @property
  def randomized_base_address_string(self):
    randomized_base_address = int(self.randomized_base_address, 10)
    if randomized_base_address == 1:
      return 'false'
    elif randomized_base_address == 2:
      return 'true'
    return ''

  @property
  def runtime_librarian_string(self):
    runtime_library = int(self.runtime_library, 10)
    if runtime_library == 2:
      return 'MultiThreadedDLL'
    if runtime_library == 3:
      return 'MultiThreadedDebugDLL'
    return ''

  @property
  def sub_system_string(self):
    sub_system = int(self.sub_system, 10)
    if sub_system == 1:
      return 'Console'
    return ''

  @property
  def target_machine_string(self):
    target_machine = int(self.target_machine, 10)
    if target_machine == 1:
      return 'MachineX86'
    # TODO: assuming here that 2 is x64.
    elif target_machine == 2:
      return 'MachineX64'
    return ''

  @property
  def warning_level_string(self):
    warning_level = int(self.warning_level, 10)
    if warning_level == 4:
      return 'Level4'
    return ''

  @property
  def whole_program_optimization_string(self):
    whole_program_optimization = int(self.whole_program_optimization, 10)
    if whole_program_optimization == 1:
      return 'true'
    return ''

  def CopyToX64(self):
    """Copies the Visual Studio project configuration to an x64 equivalent."""
    copy = VSProjectConfiguration()

    copy.basic_runtime_checks = self.basic_runtime_checks
    copy.character_set = self.character_set
    copy.compile_as = self.compile_as
    copy.data_execution_prevention = self.data_execution_prevention
    copy.debug_information_format = self.debug_information_format
    copy.dependencies = self.dependencies
    copy.dependencies_set = self.dependencies_set
    copy.enable_comdat_folding = self.enable_comdat_folding
    copy.generate_debug_information = self.generate_debug_information
    copy.import_library = self.import_library
    copy.include_directories = self.include_directories
    copy.librarian_ignore_defaults = self.librarian_ignore_defaults
    copy.librarian_output_file = self.librarian_output_file
    copy.library_directories = self.library_directories
    copy.link_incremental = self.link_incremental
    copy.linker_output_directory = self.linker_output_directory
    copy.linker_output_file = self.linker_output_file
    copy.linker_values_set = self.linker_values_set
    copy.name = self.name
    copy.optimize_references = self.optimize_references
    copy.optimization = self.optimization
    copy.output_type = self.output_type
    copy.platform = 'x64'
    copy.platform_toolset = 'Windows7.1SDK'
    copy.precompiled_header = self.precompiled_header
    copy.preprocessor_definitions = self.preprocessor_definitions
    copy.randomized_base_address = self.randomized_base_address
    copy.runtime_library = self.runtime_library
    copy.smaller_type_check = self.smaller_type_check
    copy.sub_system = self.sub_system
    copy.target_machine = '2'
    copy.warning_as_error = self.warning_as_error
    copy.warning_level = self.warning_level
    copy.whole_program_optimization = self.whole_program_optimization

    return copy


class VSProjectInformation(object):
  """Class to represent a Visual Studio project information."""

  def __init__(self):
    """Initializes Visual Studio project information."""
    self.project_guid = ''
    self.root_name_space = ''
    self.keyword = ''


class VSSolutionConfiguration(VSConfiguration):
  """Class to represent a Visual Studio solution configuration."""

  def CopyToX64(self):
    """Copies the Visual Studio solution configuration to an x64 equivalent."""
    copy = VSSolutionConfiguration()

    copy.name = self.name
    copy.platform = 'x64'

    return copy


class VSSolutionProject(object):
  """Class to represent a Visual Studio solution project."""

  def __init__(self, name, filename, guid):
    """Initializes a Visual Studio project.

    Args:
      name: the project name.
      filename: the project filename without extension.
      guid: the project identifier (GUID).
    """
    self.name = name
    self.filename = filename
    self.guid = guid.lower()
    self.dependencies = []

  def AddDependency(self, dependency_guid):
    """Adds a dependency GUID to the project.

    Args:
      dependency_guid: the project GUID of the dependency.
    """
    self.dependencies.append(dependency_guid.lower())


class FileReader(object):
  """Class to represent a file reader."""

  def __init__(self):
    """Initializes a Visual Studio solution file reader."""
    self._line = None

  def Open(self, filename):
    """Opens the solution file.

    Args:
      filename: the filename of the solution file.
    """
    # For reading these files we don't care about the actual end of lines.
    self._file = open(filename, 'r')

  def Close(self):
    """Closes the solution file."""
    self._file.close()

  def _ReadLine(self, look_ahead=False):
    """Reads a line.

    Args:
      look_ahead: optional boolean value to indicate the line
                  should be considered read (False) or not (True).
                  The default is to consider the line read.

    Returns:
      The line stripped of leading and trailing white space
      or None if no input is available.
    """
    if self._line != None:
      line = self._line
      if not look_ahead:
        self._line = None

    else:
      line = self._file.readline()
      if line:
        line = line.strip()
      if look_ahead:
        self._line = line

    return line


class VSProjectFileReader(FileReader):
  """Class to represent a Visual Studio project file reader."""


class VS2008ProjectFileReader(VSProjectFileReader):
  """Class to represent a Visual Studio 2008 project file reader."""

  def ReadHeader(self):
    """Reads a file header.

    Returns:
      True if successful or false otherwise.
    """
    line = self._ReadLine()

    if not line:
      return False

    if not line.startswith('<?xml version="1.0"'):
      return False

    # TODO check encoding?

    line = self._ReadLine()

    if not line:
      return False

    if not line.startswith('<VisualStudioProject'):
      return False

    line = self._ReadLine()

    if not line:
      return False

    if not line.startswith('ProjectType="Visual C++"'):
      return False

    line = self._ReadLine()

    if not line:
      return False

    if not line.startswith('Version="9,00"'):
      return False

    return True

  def ReadProjectInformation(self):
    """Reads project information.

    Returns:
      Project information (instance of VSProjectInformation) if successful
      or None otherwise.
    """
    project_information = VSProjectInformation()

    line = self._ReadLine()
    while line:
      if line.startswith('>'):
        break

      elif line.startswith('ProjectGUID='):
        values = re.findall('ProjectGUID="([^"]*)"', line)
        if len(values) == 1:
          project_information.project_guid = values[0]

      elif line.startswith('RootNamespace='):
        values = re.findall('RootNamespace="([^"]*)"', line)
        if len(values) == 1:
          project_information.root_name_space = values[0]

      elif line.startswith('Keyword='):
        values = re.findall('Keyword="([^"]*)"', line)
        if len(values) == 1:
          project_information.keyword = values[0]

      line = self._ReadLine()

    return project_information

  def ReadConfigurations(self):
    """Reads the configurations.

    Returns:
      The configurations (instance of VSConfigurations).
    """
    configurations = VSConfigurations()

    # Find the start of the configurations section.
    result = False
    line = self._ReadLine()

    while line:
      result = line.startswith('<Configurations>')
      if result:
        break
      line = self._ReadLine()

    if result:
      while line:
        line = self._ReadLine()

        if line.startswith('</Configurations>'):
          break

        elif line.startswith('<Configuration'):
          configuration = self._ReadConfiguration(line)

          if configuration:
            configurations.Append(configuration)

    return configurations

  def _ReadConfiguration(self, line):
    """Reads a configuration.

    Args:
      line: the line that contains the start of the configuration section.

    Returns:
      A configuration (instance of VSProjectConfiguration) or None if
      no configuration was found.
    """
    if not line.startswith('<Configuration'):
      return None

    configuration = VSProjectConfiguration()

    found_tool = False
    found_tool_compiler = False
    found_tool_librarian = False
    found_tool_linker = False

    while line:
      line = self._ReadLine()

      if line.startswith('</Configuration>'):
        break

      elif found_tool:
        if line.startswith('/>'):
          found_tool = False
          found_tool_compiler = False
          found_tool_librarian = False
          found_tool_linker = False

        elif found_tool_compiler:
          # Parse the compiler specific configuration.
          if line.startswith('Optimization='):
            values = re.findall('Optimization="([^"]*)"', line)
            if len(values) == 1:
              configuration.optimization = values[0]

          elif line.startswith('AdditionalIncludeDirectories='):
            values = re.findall(
                'AdditionalIncludeDirectories="([^"]*)"', line)
            if len(values) == 1:
              configuration.include_directories = values[0]

          elif line.startswith('PreprocessorDefinitions='):
            values = re.findall(
                'PreprocessorDefinitions="([^"]*)"', line)
            if len(values) == 1:
              configuration.preprocessor_definitions = values[0]

          elif line.startswith('BasicRuntimeChecks='):
            values = re.findall('BasicRuntimeChecks="([^"]*)"', line)
            if len(values) == 1:
              configuration.basic_runtime_checks = values[0]

          elif line.startswith('SmallerTypeCheck='):
            values = re.findall('SmallerTypeCheck="([^"]*)"', line)
            if len(values) == 1:
              configuration.smaller_type_check = values[0]

          elif line.startswith('RuntimeLibrary='):
            values = re.findall('RuntimeLibrary="([^"]*)"', line)
            if len(values) == 1:
              configuration.runtime_library = values[0]

          elif line.startswith('UsePrecompiledHeader='):
            values = re.findall('UsePrecompiledHeader="([^"]*)"', line)
            if len(values) == 1:
              configuration.precompiled_header = values[0]

          elif line.startswith('WarningLevel='):
            values = re.findall('WarningLevel="([^"]*)"', line)
            if len(values) == 1:
              configuration.warning_level = values[0]

          elif line.startswith('WarnAsError='):
            values = re.findall('WarnAsError="([^"]*)"', line)
            if len(values) == 1:
              configuration.warning_as_error = values[0]

          elif line.startswith('DebugInformationFormat='):
            values = re.findall(
                'DebugInformationFormat="([^"]*)"', line)
            if len(values) == 1:
              configuration.debug_information_format = values[0]

          elif line.startswith('CompileAs='):
            values = re.findall('CompileAs="([^"]*)"', line)
            if len(values) == 1:
              configuration.compile_as = values[0]

        elif found_tool_librarian:
          # Parse the libararian specific configuration.
          if line.startswith('OutputFile='):
            values = re.findall('OutputFile="([^"]*)"', line)
            if len(values) == 1:
              configuration.librarian_output_file = values[0]

          elif line.startswith('IgnoreAllDefaultLibraries='):
            values = re.findall(
                'IgnoreAllDefaultLibraries="([^"]*)"', line)
            if len(values) == 1:
              configuration.librarian_ignore_defaults = values[0]

        elif found_tool_linker:
          # Parse the linker specific configuration.
          if line.startswith('OutputDirectory='):
            configuration.linker_values_set = True
            values = re.findall('OutputDirectory="([^"]*)"', line)
            if len(values) == 1:
              configuration.linker_output_directory = values[0]

          elif line.startswith('OutputFile='):
            configuration.linker_values_set = True
            values = re.findall('OutputFile="([^"]*)"', line)
            if len(values) == 1:
              configuration.linker_output_file = values[0]

          elif line.startswith('AdditionalDependencies='):
            configuration.linker_values_set = True
            values = re.findall(
                'AdditionalDependencies="([^"]*)"', line)
            if len(values) == 1:
              configuration.dependencies = values[0]
              configuration.dependencies_set = True

          elif line.startswith('LinkIncremental='):
            configuration.linker_values_set = True
            values = re.findall('LinkIncremental="([^"]*)"', line)
            if len(values) == 1:
              configuration.link_incremental = values[0]

          elif line.startswith('AdditionalLibraryDirectories='):
            configuration.linker_values_set = True
            values = re.findall(
                'AdditionalLibraryDirectories="([^"]*)"', line)
            if len(values) == 1:
              configuration.library_directories = values[0]

          elif line.startswith('GenerateDebugInformation='):
            configuration.linker_values_set = True
            values = re.findall(
                'GenerateDebugInformation="([^"]*)"', line)
            if len(values) == 1:
              configuration.generate_debug_information = values[0]

          elif line.startswith('SubSystem='):
            configuration.linker_values_set = True
            values = re.findall(
                'SubSystem="([^"]*)"', line)
            if len(values) == 1:
              configuration.sub_system = values[0]

          elif line.startswith('OptimizeReferences='):
            configuration.linker_values_set = True
            values = re.findall(
                'OptimizeReferences="([^"]*)"', line)
            if len(values) == 1:
              configuration.optimize_references = values[0]

          elif line.startswith('RandomizedBaseAddress='):
            configuration.linker_values_set = True
            values = re.findall(
                'RandomizedBaseAddress="([^"]*)"', line)
            if len(values) == 1:
              configuration.randomized_base_address = values[0]

          elif line.startswith('EnableCOMDATFolding='):
            configuration.linker_values_set = True
            values = re.findall(
                'EnableCOMDATFolding="([^"]*)"', line)
            if len(values) == 1:
              configuration.enable_comdat_folding = values[0]

          elif line.startswith('DataExecutionPrevention='):
            configuration.linker_values_set = True
            values = re.findall(
                'DataExecutionPrevention="([^"]*)"', line)
            if len(values) == 1:
              configuration.data_execution_prevention = values[0]

          elif line.startswith('ImportLibrary='):
            configuration.linker_values_set = True
            values = re.findall(
                'ImportLibrary="([^"]*)"', line)
            if len(values) == 1:
              configuration.import_library = values[0]

          elif line.startswith('TargetMachine='):
            configuration.linker_values_set = True
            values = re.findall('TargetMachine="([^"]*)"', line)
            if len(values) == 1:
              configuration.target_machine = values[0]

        elif line.startswith('Name="VCCLCompilerTool"'):
          found_tool_compiler = True

        elif line.startswith('Name="VCLibrarianTool"'):
          found_tool_librarian = True

        elif line.startswith('Name="VCLinkerTool"'):
          found_tool_linker = True

      elif line.startswith('<Tool'):
        found_tool = True

      elif line.startswith('Name='):
        # For more than 1 match findall will return a list with a tuple.
        values = re.findall('Name="([^|]*)[|]([^"]*)"', line)[0]
        if len(values) == 2:
          configuration.name = values[0]
          configuration.platform = values[1]

      elif line.startswith('ConfigurationType='):
        values = re.findall('ConfigurationType="([^"]*)"', line)
        if len(values) == 1:
          configuration.output_type = values[0]

      elif line.startswith('CharacterSet='):
        values = re.findall('CharacterSet="([^"]*)"', line)
        if len(values) == 1:
          configuration.character_set = values[0]

      elif line.startswith('WholeProgramOptimization='):
        values = re.findall('WholeProgramOptimization="([^"]*)"', line)
        if len(values) == 1:
          configuration.whole_program_optimization = values[0]

      # TODO: PlatformToolset.

    # Add the target machine when not defined.
    if not configuration.target_machine:
      if configuration.platform == 'Win32':
        configuration.target_machine = '1'
      # TODO: assuming here that 2 is x64.
      elif configuration.platform == 'x64':
        configuration.target_machine = '2'

    return configuration

  def ReadFiles(self):
    """Reads the files.

    Returns:
{'fraction_non_alphanumeric': 0.10655166762271573, 'fraction_numerical': 0.008870769482914037, 'mean_word_length': 2.79542803825519, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 2, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '188028', 'n_tokens_mistral': 14190, 'n_tokens_neox': 13381, 'n_words': 3322}