internal ReferenceTable(bool findDependencies, bool findSatellites, bool findSerializationAssemblies, bool findRelatedFiles, string[] searchPaths, string[] allowedAssemblyExtensions, string[] relatedFileExtensions, string[] candidateAssemblyFiles, string[] frameworkPaths, InstalledAssemblies installedAssemblies, System.Reflection.ProcessorArchitecture targetProcessorArchitecture, Microsoft.Build.Shared.FileExists fileExists, Microsoft.Build.Shared.DirectoryExists directoryExists, Microsoft.Build.Tasks.GetDirectories getDirectories, GetAssemblyName getAssemblyName, GetAssemblyMetadata getAssemblyMetadata, GetRegistrySubKeyNames getRegistrySubKeyNames, GetRegistrySubKeyDefaultValue getRegistrySubKeyDefaultValue, OpenBaseKey openBaseKey, GetAssemblyRuntimeVersion getRuntimeVersion, Version targetedRuntimeVersion, Version projectTargetFramework, FrameworkName targetFrameworkMoniker, TaskLoggingHelper log, string[] latestTargetFrameworkDirectories, bool copyLocalDependenciesWhenParentReferenceInGac, CheckIfAssemblyInGac checkIfAssemblyIsInGac)
 {
     this.log = log;
     this.findDependencies = findDependencies;
     this.findSatellites = findSatellites;
     this.findSerializationAssemblies = findSerializationAssemblies;
     this.findRelatedFiles = findRelatedFiles;
     this.frameworkPaths = frameworkPaths;
     this.allowedAssemblyExtensions = allowedAssemblyExtensions;
     this.relatedFileExtensions = relatedFileExtensions;
     this.installedAssemblies = installedAssemblies;
     this.targetProcessorArchitecture = targetProcessorArchitecture;
     this.fileExists = fileExists;
     this.directoryExists = directoryExists;
     this.getDirectories = getDirectories;
     this.getAssemblyName = getAssemblyName;
     this.getAssemblyMetadata = getAssemblyMetadata;
     this.getRuntimeVersion = getRuntimeVersion;
     this.projectTargetFramework = projectTargetFramework;
     this.targetedRuntimeVersion = targetedRuntimeVersion;
     this.openBaseKey = openBaseKey;
     this.targetFrameworkMoniker = targetFrameworkMoniker;
     this.latestTargetFrameworkDirectories = latestTargetFrameworkDirectories;
     this.copyLocalDependenciesWhenParentReferenceInGac = copyLocalDependenciesWhenParentReferenceInGac;
     this.checkIfAssemblyIsInGac = checkIfAssemblyIsInGac;
     this.compiledSearchPaths = AssemblyResolution.CompileSearchPaths(searchPaths, candidateAssemblyFiles, targetProcessorArchitecture, frameworkPaths, fileExists, getAssemblyName, getRegistrySubKeyNames, getRegistrySubKeyDefaultValue, openBaseKey, installedAssemblies, getRuntimeVersion, targetedRuntimeVersion);
 }
Esempio n. 2
0
        /// <summary>
        /// Construct.
        /// </summary>
        /// <param name="registryKeyRoot">Like Software\Microsoft\[.NetFramework | .NetCompactFramework]</param>
        /// <param name="targetRuntimeVersion">The runtime version property from the project file.</param>
        /// <param name="registryKeySuffix">Like [ PocketPC | SmartPhone | WindowsCE]\AssemblyFoldersEx</param>
        /// <param name="getRegistrySubKeyNames">Used to find registry subkey names.</param>
        /// <param name="getRegistrySubKeyDefaultValue">Used to find registry key default values.</param>
        internal AssemblyFoldersEx
        (
            string registryKeyRoot,
            string targetRuntimeVersion,
            string registryKeySuffix,
            string osVersion,
            string platform,
            GetRegistrySubKeyNames getRegistrySubKeyNames,
            GetRegistrySubKeyDefaultValue getRegistrySubKeyDefaultValue,
            ProcessorArchitecture targetProcessorArchitecture,
            OpenBaseKey openBaseKey
        )
        {
            bool is64bitOS      = Environment.Is64BitOperatingSystem;
            bool targeting64bit = targetProcessorArchitecture == ProcessorArchitecture.Amd64 || targetProcessorArchitecture == ProcessorArchitecture.IA64;

            // The registry lookup should be as follows:

            /* 64 bit OS:
             *            Targeting 64 bit:
             *                First, look in 64 bit registry location
             *                Second, look in 32 bit registry location
             *            Targeting X86 or MSIL:
             *                First,  look in the 32 bit hive
             *                Second, look in 64 bit hive
             *
             *  32 bit OS:
             *        32 bit process:
             *            Targeting 64 bit, or X86, or MSIL:
             *                Look in the default registy which is the 32 bit hive
             */

            // Under WOW64 the HKEY_CURRENT_USER\SOFTWARE key is shared. This means the values are the same in the 64 bit and 32 bit views. This means we only need to get one view of this key.
            FindDirectories(RegistryView.Default, RegistryHive.CurrentUser, registryKeyRoot, targetRuntimeVersion, registryKeySuffix, osVersion, platform, getRegistrySubKeyNames, getRegistrySubKeyDefaultValue, openBaseKey);

            if (is64bitOS)
            {
                // Under WOW64 the HKEY_LOCAL_MACHINE\SOFTWARE key is redirected. This means the values can be different in the 64 bit and 32 bit views. This means we only need to get look at both keys.

                if (targeting64bit)
                {
                    FindDirectories(RegistryView.Registry64, RegistryHive.LocalMachine, registryKeyRoot, targetRuntimeVersion, registryKeySuffix, osVersion, platform, getRegistrySubKeyNames, getRegistrySubKeyDefaultValue, openBaseKey);
                    FindDirectories(RegistryView.Registry32, RegistryHive.LocalMachine, registryKeyRoot, targetRuntimeVersion, registryKeySuffix, osVersion, platform, getRegistrySubKeyNames, getRegistrySubKeyDefaultValue, openBaseKey);
                }
                else
                {
                    FindDirectories(RegistryView.Registry32, RegistryHive.LocalMachine, registryKeyRoot, targetRuntimeVersion, registryKeySuffix, osVersion, platform, getRegistrySubKeyNames, getRegistrySubKeyDefaultValue, openBaseKey);
                    FindDirectories(RegistryView.Registry64, RegistryHive.LocalMachine, registryKeyRoot, targetRuntimeVersion, registryKeySuffix, osVersion, platform, getRegistrySubKeyNames, getRegistrySubKeyDefaultValue, openBaseKey);
                }
            }
            else
            {
                FindDirectories(RegistryView.Default, RegistryHive.LocalMachine, registryKeyRoot, targetRuntimeVersion, registryKeySuffix, osVersion, platform, getRegistrySubKeyNames, getRegistrySubKeyDefaultValue, openBaseKey);
            }
        }
Esempio n. 3
0
 public static System.Collections.Generic.IList <Microsoft.Build.Utilities.AssemblyFoldersFromConfigInfo> GetAssemblyFoldersFromConfigInfo(string configFile, string targetFrameworkVersion, System.Reflection.ProcessorArchitecture targetProcessorArchitecture)
 {
     throw null;
 }
Esempio n. 4
0
        /// <summary>
        /// Construct.
        /// </summary>
        /// <param name="findDependencies">If true, then search for dependencies.</param>
        /// <param name="findSatellites">If true, then search for satellite files.</param>
        /// <param name="findSerializatoinAssemblies">If true, then search for serialization assembly files.</param>
        /// <param name="findRelatedFiles">If true, then search for related files.</param>
        /// <param name="searchPaths">Paths to search for dependent assemblies on.</param>
        /// <param name="candidateAssemblyFiles">List of literal assembly file names to be considered when SearchPaths has {CandidateAssemblyFiles}.</param>
        /// <param name="resolvedSDKItems">Resolved sdk items</param>
        /// <param name="frameworkPaths">Path to the FX.</param>
        /// <param name="installedAssemblies">Installed assembly XML tables.</param>
        /// <param name="targetProcessorArchitecture">Like x86 or IA64\AMD64, the processor architecture being targetted.</param>
        /// <param name="fileExists">Delegate used for checking for the existence of a file.</param>
        /// <param name="directoryExists">Delegate used for files.</param>
        /// <param name="getDirectories">Delegate used for getting directories.</param>
        /// <param name="getAssemblyName">Delegate used for getting assembly names.</param>
        /// <param name="getAssemblyMetadata">Delegate used for finding dependencies of a file.</param>
        /// <param name="getRegistrySubKeyNames">Used to get registry subkey names.</param>
        /// <param name="getRegistrySubKeyDefaultValue">Used to get registry default values.</param>
        internal ReferenceTable
        (
                      IBuildEngine buildEngine,
            bool findDependencies,
            bool findSatellites,
            bool findSerializationAssemblies,
            bool findRelatedFiles,
            string[] searchPaths,
            string[] allowedAssemblyExtensions,
            string[] relatedFileExtensions,
            string[] candidateAssemblyFiles,
            ITaskItem[] resolvedSDKItems,
            string[] frameworkPaths,
            InstalledAssemblies installedAssemblies,
            System.Reflection.ProcessorArchitecture targetProcessorArchitecture,
            FileExists fileExists,
            DirectoryExists directoryExists,
            GetDirectories getDirectories,
            GetAssemblyName getAssemblyName,
            GetAssemblyMetadata getAssemblyMetadata,
            GetRegistrySubKeyNames getRegistrySubKeyNames,
            GetRegistrySubKeyDefaultValue getRegistrySubKeyDefaultValue,
            OpenBaseKey openBaseKey,
            GetAssemblyRuntimeVersion getRuntimeVersion,
            Version targetedRuntimeVersion,
            Version projectTargetFramework,
            FrameworkNameVersioning targetFrameworkMoniker,
            TaskLoggingHelper log,
            string[] latestTargetFrameworkDirectories,
            bool copyLocalDependenciesWhenParentReferenceInGac,
            bool doNotCopyLocalIfInGac,
            GetAssemblyPathInGac getAssemblyPathInGac,
            IsWinMDFile isWinMDFile,
            bool ignoreVersionForFrameworkReferences,
            ReadMachineTypeFromPEHeader readMachineTypeFromPEHeader,
            WarnOrErrorOnTargetArchitectureMismatchBehavior warnOrErrorOnTargetArchitectureMismatch,
            bool ignoreFrameworkAttributeVersionMismatch,
            bool unresolveFrameworkAssembliesFromHigherFrameworks
        )
        {
            _buildEngine = buildEngine;
            _log = log;
            _findDependencies = findDependencies;
            _findSatellites = findSatellites;
            _findSerializationAssemblies = findSerializationAssemblies;
            _findRelatedFiles = findRelatedFiles;
            _frameworkPaths = frameworkPaths;
            _allowedAssemblyExtensions = allowedAssemblyExtensions;
            _relatedFileExtensions = relatedFileExtensions;
            _installedAssemblies = installedAssemblies;
            _targetProcessorArchitecture = targetProcessorArchitecture;
            _fileExists = fileExists;
            _directoryExists = directoryExists;
            _getDirectories = getDirectories;
            _getAssemblyName = getAssemblyName;
            _getAssemblyMetadata = getAssemblyMetadata;
            _getRuntimeVersion = getRuntimeVersion;
            _projectTargetFramework = projectTargetFramework;
            _targetedRuntimeVersion = targetedRuntimeVersion;
            _openBaseKey = openBaseKey;
            _targetFrameworkMoniker = targetFrameworkMoniker;
            _latestTargetFrameworkDirectories = latestTargetFrameworkDirectories;
            _copyLocalDependenciesWhenParentReferenceInGac = copyLocalDependenciesWhenParentReferenceInGac;
            _doNotCopyLocalIfInGac = doNotCopyLocalIfInGac;
            _getAssemblyPathInGac = getAssemblyPathInGac;
            _isWinMDFile = isWinMDFile;
            _readMachineTypeFromPEHeader = readMachineTypeFromPEHeader;
            _warnOrErrorOnTargetArchitectureMismatch = warnOrErrorOnTargetArchitectureMismatch;
            _ignoreFrameworkAttributeVersionMismatch = ignoreFrameworkAttributeVersionMismatch;

            // Set condition for when to check assembly version against the target framework version 
            _checkAssemblyVersionAgainstTargetFrameworkVersion = unresolveFrameworkAssembliesFromHigherFrameworks || ((_projectTargetFramework ?? ReferenceTable.s_targetFrameworkVersion_40) <= ReferenceTable.s_targetFrameworkVersion_40);

            // Convert the list of installed SDK's to a dictionary for faster lookup
            _resolvedSDKReferences = new Dictionary<string, ITaskItem>(StringComparer.OrdinalIgnoreCase);
            _ignoreVersionForFrameworkReferences = ignoreVersionForFrameworkReferences;


            if (resolvedSDKItems != null)
            {
                foreach (ITaskItem resolvedSDK in resolvedSDKItems)
                {
                    string sdkName = resolvedSDK.GetMetadata("SDKName");

                    if (sdkName.Length > 0)
                    {
                        if (!_resolvedSDKReferences.ContainsKey(sdkName))
                        {
                            _resolvedSDKReferences.Add(sdkName, resolvedSDK);
                        }
                        else
                        {
                            _resolvedSDKReferences[sdkName] = resolvedSDK;
                        }
                    }
                }
            }

            // Compile searchpaths into fast resolver array.
            _compiledSearchPaths = AssemblyResolution.CompileSearchPaths
                (
                    buildEngine,
                    searchPaths,
                    candidateAssemblyFiles,
                    targetProcessorArchitecture,
                    frameworkPaths,
                    fileExists,
                    getAssemblyName,
                    getRegistrySubKeyNames,
                    getRegistrySubKeyDefaultValue,
                    openBaseKey,
                    installedAssemblies,
                    getRuntimeVersion,
                    targetedRuntimeVersion,
                    getAssemblyPathInGac
                );
        }
 /// <summary>
 ///  Checks to see if the assemblyName passed in is in the GAC.
 /// </summary>
 private string GetAssemblyPathInGac(AssemblyNameExtension assemblyName, SystemProcessorArchitecture targetProcessorArchitecture, GetAssemblyRuntimeVersion getRuntimeVersion, Version targetedRuntimeVersion, FileExists fileExists, bool fullFusionName, bool specificVersion)
 {
     return GlobalAssemblyCache.GetLocation(BuildEngine as IBuildEngine4, assemblyName, targetProcessorArchitecture, getRuntimeVersion, targetedRuntimeVersion, fullFusionName, fileExists, null, null, specificVersion /* this value does not matter if we are passing a full fusion name*/);
 }
 /// <summary>
 /// Take a processor architecure and get the string representation back.
 /// </summary>
 internal static string ProcessorArchitectureToString(SystemProcessorArchitecture processorArchitecture)
 {
     if (SystemProcessorArchitecture.Amd64 == processorArchitecture)
     {
         return Microsoft.Build.Utilities.ProcessorArchitecture.AMD64;
     }
     else if (SystemProcessorArchitecture.IA64 == processorArchitecture)
     {
         return Microsoft.Build.Utilities.ProcessorArchitecture.IA64;
     }
     else if (SystemProcessorArchitecture.MSIL == processorArchitecture)
     {
         return Microsoft.Build.Utilities.ProcessorArchitecture.MSIL;
     }
     else if (SystemProcessorArchitecture.X86 == processorArchitecture)
     {
         return Microsoft.Build.Utilities.ProcessorArchitecture.X86;
     }
     else if (SystemProcessorArchitecture.Arm == processorArchitecture)
     {
         return Microsoft.Build.Utilities.ProcessorArchitecture.ARM;
     }
     return String.Empty;
 }
Esempio n. 7
0
        /// <summary>
        /// Compile search paths into an array of resolvers.
        /// </summary>
        /// <param name="searchPaths"></param>
        /// <param name="candidateAssemblyFiles">Paths to assembly files mentioned in the project.</param>
        /// <param name="targetProcessorArchitecture">Like x86 or IA64\AMD64, the processor architecture being targetted.</param>
        /// <param name="frameworkPaths">Paths to FX folders.</param>
        /// <param name="fileExists"></param>
        /// <param name="getAssemblyName"></param>
        /// <param name="getRegistrySubKeyNames"></param>
        /// <param name="getRegistrySubKeyDefaultValue"></param>
        /// <param name="installedAssemblies"></param>
        /// <returns></returns>
        public static Resolver[] CompileSearchPaths
        (
            IBuildEngine buildEngine,
            string[] searchPaths,
            string[] candidateAssemblyFiles,
            System.Reflection.ProcessorArchitecture targetProcessorArchitecture,
            string[] frameworkPaths,
            FileExists fileExists,
            GetAssemblyName getAssemblyName,
            GetRegistrySubKeyNames getRegistrySubKeyNames,
            GetRegistrySubKeyDefaultValue getRegistrySubKeyDefaultValue,
            OpenBaseKey openBaseKey,
            InstalledAssemblies installedAssemblies,
            GetAssemblyRuntimeVersion getRuntimeVersion,
            Version targetedRuntimeVersion
        )
        {
            Resolver[] resolvers = new Resolver[searchPaths.Length];

            for (int p = 0; p < searchPaths.Length; ++p)
            {
                string basePath = searchPaths[p];

                // Was {HintPathFromItem} specified? If so, take the Item's
                // HintPath property.
                if (0 == String.Compare(basePath, AssemblyResolutionConstants.hintPathSentinel, StringComparison.OrdinalIgnoreCase))
                {
                    resolvers[p] = new HintPathResolver(searchPaths[p], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion);
                }
                else if (0 == String.Compare(basePath, AssemblyResolutionConstants.frameworkPathSentinel, StringComparison.OrdinalIgnoreCase))
                {
                    resolvers[p] = new FrameworkPathResolver(frameworkPaths, installedAssemblies, searchPaths[p], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion);
                }
                else if (0 == String.Compare(basePath, AssemblyResolutionConstants.rawFileNameSentinel, StringComparison.OrdinalIgnoreCase))
                {
                    resolvers[p] = new RawFilenameResolver(searchPaths[p], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion);
                }
                else if (0 == String.Compare(basePath, AssemblyResolutionConstants.candidateAssemblyFilesSentinel, StringComparison.OrdinalIgnoreCase))
                {
                    resolvers[p] = new CandidateAssemblyFilesResolver(candidateAssemblyFiles, searchPaths[p], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion);
                }
                else if (0 == String.Compare(basePath, AssemblyResolutionConstants.gacSentinel, StringComparison.OrdinalIgnoreCase))
                {
                    resolvers[p] = new GacResolver(targetProcessorArchitecture, searchPaths[p], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion, buildEngine);
                }
                else if (0 == String.Compare(basePath, AssemblyResolutionConstants.assemblyFoldersSentinel, StringComparison.OrdinalIgnoreCase))
                {
                    resolvers[p] = new AssemblyFoldersResolver(searchPaths[p], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion);
                }
                // Check for AssemblyFoldersEx sentinel.
                else if (0 == String.Compare(basePath, 0, AssemblyResolutionConstants.assemblyFoldersExSentinel, 0, AssemblyResolutionConstants.assemblyFoldersExSentinel.Length, StringComparison.OrdinalIgnoreCase))
                {
                    resolvers[p] = new AssemblyFoldersExResolver(searchPaths[p], getAssemblyName, fileExists, getRegistrySubKeyNames, getRegistrySubKeyDefaultValue, getRuntimeVersion, openBaseKey, targetedRuntimeVersion, targetProcessorArchitecture, true, buildEngine);
                }
                else
                {
                    resolvers[p] = new DirectoryResolver(searchPaths[p], getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVersion);
                }
            }
            return(resolvers);
        }
Esempio n. 8
0
 /// <summary>
 /// Construct.
 /// </summary>
 /// <param name="targetProcessorArchitecture">Like x86 or IA64\AMD64, the processor architecture being targetted.</param>
 public GacResolver(System.Reflection.ProcessorArchitecture targetProcessorArchitecture, string searchPathElement, GetAssemblyName getAssemblyName, FileExists fileExists, GetAssemblyRuntimeVersion getRuntimeVersion, Version targetedRuntimeVesion, IBuildEngine buildEngine)
     : base(searchPathElement, getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVesion, targetProcessorArchitecture, true)
 {
     _buildEngine = buildEngine as IBuildEngine4;
 }
Esempio n. 9
0
 /// <summary>
 /// Construct.
 /// </summary>
 public AssemblyFoldersExResolver(string searchPathElement, GetAssemblyName getAssemblyName, FileExists fileExists, GetRegistrySubKeyNames getRegistrySubKeyNames, GetRegistrySubKeyDefaultValue getRegistrySubKeyDefaultValue, GetAssemblyRuntimeVersion getRuntimeVersion, OpenBaseKey openBaseKey, Version targetedRuntimeVesion, ProcessorArchitecture targetProcessorArchitecture, bool compareProcessorArchitecture, IBuildEngine buildEngine)
     : base(searchPathElement, getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVesion, targetProcessorArchitecture, compareProcessorArchitecture)
 {
     _buildEngine                   = buildEngine as IBuildEngine4;
     _getRegistrySubKeyNames        = getRegistrySubKeyNames;
     _getRegistrySubKeyDefaultValue = getRegistrySubKeyDefaultValue;
     _openBaseKey                   = openBaseKey;
 }
 /// <summary>
 /// Construct.
 /// </summary>
 public AssemblyFoldersExResolver(string searchPathElement, GetAssemblyName getAssemblyName, FileExists fileExists, GetRegistrySubKeyNames getRegistrySubKeyNames, GetRegistrySubKeyDefaultValue getRegistrySubKeyDefaultValue, GetAssemblyRuntimeVersion getRuntimeVersion, OpenBaseKey openBaseKey, Version targetedRuntimeVesion, ProcessorArchitecture targetProcessorArchitecture, bool compareProcessorArchitecture, IBuildEngine buildEngine)
     : base(searchPathElement, getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVesion, targetProcessorArchitecture, compareProcessorArchitecture)
 {
     _buildEngine = buildEngine as IBuildEngine4;
     _getRegistrySubKeyNames = getRegistrySubKeyNames;
     _getRegistrySubKeyDefaultValue = getRegistrySubKeyDefaultValue;
     _openBaseKey = openBaseKey;
 }
Esempio n. 11
0
 /// <summary>
 /// Construct.
 /// </summary>
 /// <param name="targetProcessorArchitecture">Like x86 or IA64\AMD64, the processor architecture being targeted.</param>
 /// <param name="searchPathElement">The search path element.</param>
 /// <param name="getAssemblyName">Delegate to get the assembly name object.</param>
 /// <param name="fileExists">Delegate to check if the file exists.</param>
 /// <param name="getRuntimeVersion">Delegate to get the runtime version.</param>
 /// <param name="targetedRuntimeVesion">The targeted runtime version.</param>
 /// <param name="getAssemblyPathInGac">Delegate to get assembly path in the GAC.</param>
 public GacResolver(System.Reflection.ProcessorArchitecture targetProcessorArchitecture, string searchPathElement, GetAssemblyName getAssemblyName, FileExists fileExists, GetAssemblyRuntimeVersion getRuntimeVersion, Version targetedRuntimeVesion, GetAssemblyPathInGac getAssemblyPathInGac)
     : base(searchPathElement, getAssemblyName, fileExists, getRuntimeVersion, targetedRuntimeVesion, targetProcessorArchitecture, true)
 {
     _getAssemblyPathInGac = getAssemblyPathInGac;
 }
 /// <summary>
 ///  Checks to see if the assemblyName passed in is in the GAC.
 /// </summary>
 private bool CheckForAssemblyInGac(AssemblyNameExtension assemblyName, SystemProcessorArchitecture targetProcessorArchitecture, GetAssemblyRuntimeVersion getRuntimeVersion, Version targetedRuntimeVersion, FileExists fileExists)
 {
     string gacLocation = null;
     if (assemblyName.Version != null)
     {
         gacLocation = GlobalAssemblyCache.GetLocation(BuildEngine as IBuildEngine4, assemblyName, targetProcessorArchitecture, getRuntimeVersion, targetedRuntimeVersion, true, fileExists, null, null, false /* this value does not matter if we are passing a full fusion name*/);
     }
     return gacLocation != null;
 }