/// <summary> /// Reads a config hierarchy (or retrieve it from the cache) /// </summary> /// <param name="Type">The type of hierarchy to read</param> /// <param name="ProjectDir">The project directory to read the hierarchy for</param> /// <param name="Platform">Which platform to read platform-specific config files for</param> /// <returns>The requested config hierarchy</returns> public static ConfigHierarchy ReadHierarchy(ConfigHierarchyType Type, DirectoryReference ProjectDir, UnrealTargetPlatform Platform) { // Get the key to use for the cache. It cannot be null, so we use the engine directory if a project directory is not given. ConfigHierarchyKey Key = new ConfigHierarchyKey(Type, ProjectDir, Platform); // Try to get the cached hierarchy with this key ConfigHierarchy Hierarchy; if (!HierarchyKeyToHierarchy.TryGetValue(Key, out Hierarchy)) { List <ConfigFile> Files = new List <ConfigFile>(); foreach (FileReference IniFileName in ConfigHierarchy.EnumerateConfigFileLocations(Type, ProjectDir, Platform)) { ConfigFile File; if (TryReadFile(IniFileName, out File)) { Files.Add(File); } } Hierarchy = new ConfigHierarchy(Files); HierarchyKeyToHierarchy.Add(Key, Hierarchy); } return(Hierarchy); }
/// <summary> /// Construct a key from an archive /// </summary> /// <param name="Reader">Archive to read from</param> public ConfigDependencyKey(BinaryArchiveReader Reader) { Type = (ConfigHierarchyType)Reader.ReadInt(); ProjectDir = Reader.ReadDirectoryReference(); Platform = Reader.ReadUnrealTargetPlatform(); SectionName = Reader.ReadString(); KeyName = Reader.ReadString(); }
/// <summary> /// Constructor /// </summary> /// <param name="Type">The config hierarchy type</param> /// <param name="ProjectDir">Project directory to read config files from</param> /// <param name="Platform">The platform being built</param> /// <param name="SectionName">The section name</param> /// <param name="KeyName">The key name</param> public ConfigDependencyKey(ConfigHierarchyType Type, DirectoryReference ProjectDir, UnrealTargetPlatform Platform, string SectionName, string KeyName) { this.Type = Type; this.ProjectDir = ProjectDir; this.Platform = Platform; this.SectionName = SectionName; this.KeyName = KeyName; }
/// <summary> /// Reads a config hierarchy (or retrieve it from the cache) /// </summary> /// <param name="Type">The type of hierarchy to read</param> /// <param name="ProjectDir">The project directory to read the hierarchy for</param> /// <param name="Platform">Which platform to read platform-specific config files for</param> /// <param name="GeneratedConfigDir">Base directory for generated configs</param> /// <returns>The requested config hierarchy</returns> public static ConfigHierarchy ReadHierarchy(ConfigHierarchyType Type, DirectoryReference ProjectDir, UnrealTargetPlatform Platform, DirectoryReference GeneratedConfigDir = null) { // Get the key to use for the cache. It cannot be null, so we use the engine directory if a project directory is not given. ConfigHierarchyKey Key = new ConfigHierarchyKey(Type, ProjectDir, Platform); // Try to get the cached hierarchy with this key ConfigHierarchy Hierarchy; if (!HierarchyKeyToHierarchy.TryGetValue(Key, out Hierarchy)) { List <ConfigFile> Files = new List <ConfigFile>(); foreach (FileReference IniFileName in ConfigHierarchy.EnumerateConfigFileLocations(Type, ProjectDir, Platform)) { ConfigFile File; if (TryReadFile(IniFileName, out File)) { Files.Add(File); } } // If we haven't been given a generated project dir, but we do have a project then the generated configs // should go into ProjectDir/Saved if (GeneratedConfigDir == null && ProjectDir != null) { GeneratedConfigDir = DirectoryReference.Combine(ProjectDir, "Saved"); } if (GeneratedConfigDir != null) { // We know where the generated version of this config file lives, so we can read it back in // and include any user settings from there in our hierarchy string BaseIniName = Enum.GetName(typeof(ConfigHierarchyType), Type); string PlatformName = ConfigHierarchy.GetIniPlatformName(Platform); FileReference DestinationIniFilename = FileReference.Combine(GeneratedConfigDir, "Config", PlatformName, BaseIniName + ".ini"); ConfigFile File; if (TryReadFile(DestinationIniFilename, out File)) { Files.Add(File); } } // Handle command line overrides string[] CmdLine = Environment.GetCommandLineArgs(); string IniConfigArgPrefix = "-ini:" + Enum.GetName(typeof(ConfigHierarchyType), Type) + ":"; foreach (string CmdLineArg in CmdLine) { if (CmdLineArg.StartsWith(IniConfigArgPrefix)) { ConfigFile OverrideFile = new ConfigFile(CmdLineArg.Substring(IniConfigArgPrefix.Length)); Files.Add(OverrideFile); } } Hierarchy = new ConfigHierarchy(Files); HierarchyKeyToHierarchy.Add(Key, Hierarchy); } return(Hierarchy); }
/// <summary> /// Reads a config hierarchy (or retrieve it from the cache) /// </summary> /// <param name="Type">The type of hierarchy to read</param> /// <param name="ProjectDir">The project directory to read the hierarchy for</param> /// <param name="Platform">Which platform to read platform-specific config files for</param> /// <returns>The requested config hierarchy</returns> public static ConfigHierarchy ReadHierarchy(ConfigHierarchyType Type, DirectoryReference ProjectDir, UnrealTargetPlatform Platform) { // Get the key to use for the cache. It cannot be null, so we use the engine directory if a project directory is not given. ConfigHierarchyKey Key = new ConfigHierarchyKey(Type, ProjectDir, Platform); // Try to get the cached hierarchy with this key ConfigHierarchy Hierarchy; lock (HierarchyKeyToHierarchy) { if (!HierarchyKeyToHierarchy.TryGetValue(Key, out Hierarchy)) { // Find all the input files List <ConfigFile> Files = new List <ConfigFile>(); foreach (FileReference IniFileName in ConfigHierarchy.EnumerateConfigFileLocations(Type, ProjectDir, Platform)) { ConfigFile File; if (TryReadFile(IniFileName, out File)) { Files.Add(File); } } // Handle command line overrides string[] CmdLine = Environment.GetCommandLineArgs(); string IniConfigArgPrefix = "-ini:" + Enum.GetName(typeof(ConfigHierarchyType), Type) + ":"; foreach (string CmdLineArg in CmdLine) { if (CmdLineArg.StartsWith(IniConfigArgPrefix)) { ConfigFile OverrideFile = new ConfigFile(CmdLineArg.Substring(IniConfigArgPrefix.Length)); Files.Add(OverrideFile); } } // Create the hierarchy Hierarchy = new ConfigHierarchy(Files); HierarchyKeyToHierarchy.Add(Key, Hierarchy); } } return(Hierarchy); }
/// <summary> /// Returns a list of INI filenames for the given project /// </summary> public static IEnumerable <FileReference> EnumerateConfigFileLocations(ConfigHierarchyType Type, DirectoryReference ProjectDir, UnrealTargetPlatform Platform) { string BaseIniName = Enum.GetName(typeof(ConfigHierarchyType), Type); string PlatformName = GetIniPlatformName(Platform); // Engine/Config/Base.ini (included in every ini type, required) yield return(FileReference.Combine(UnrealBuildTool.EngineDirectory, "Config", "Base.ini")); // Engine/Config/Base* ini yield return(FileReference.Combine(UnrealBuildTool.EngineDirectory, "Config", "Base" + BaseIniName + ".ini")); if (Platform != UnrealTargetPlatform.Unknown) { // Engine/Config/Platform/BasePlatform* ini yield return(FileReference.Combine(UnrealBuildTool.EngineDirectory, "Config", PlatformName, "Base" + PlatformName + BaseIniName + ".ini")); } // Engine/Config/NotForLicensees/Base* ini yield return(FileReference.Combine(UnrealBuildTool.EngineDirectory, "Config", "NotForLicensees", "Base" + BaseIniName + ".ini")); // NOTE: 4.7: See comment in GetSourceIniHierarchyFilenames() // Engine/Config/NoRedist/Base* ini // yield return Path.Combine(EngineDirectory, "Config", "NoRedist", "Base" + BaseIniName + ".ini"); if (ProjectDir != null) { // Game/Config/Default* ini yield return(FileReference.Combine(ProjectDir, "Config", "Default" + BaseIniName + ".ini")); // Game/Config/NotForLicensees/Default* ini yield return(FileReference.Combine(ProjectDir, "Config", "NotForLicensees", "Default" + BaseIniName + ".ini")); // Game/Config/NoRedist/Default* ini yield return(FileReference.Combine(ProjectDir, "Config", "NoRedist", "Default" + BaseIniName + ".ini")); } if (Platform != UnrealTargetPlatform.Unknown) { // Engine/Config/Platform/Platform* ini yield return(FileReference.Combine(UnrealBuildTool.EngineDirectory, "Config", PlatformName, PlatformName + BaseIniName + ".ini")); // Engine/Config/NotForLicensees/Platform/Platform* ini yield return(FileReference.Combine(UnrealBuildTool.EngineDirectory, "Config", "NotForLicensees", PlatformName, PlatformName + BaseIniName + ".ini")); // Engine/Config/NoRedist/Platform/Platform* ini yield return(FileReference.Combine(UnrealBuildTool.EngineDirectory, "Config", "NoRedist", PlatformName, PlatformName + BaseIniName + ".ini")); if (ProjectDir != null) { // Game/Config/Platform/Platform* ini yield return(FileReference.Combine(ProjectDir, "Config", PlatformName, PlatformName + BaseIniName + ".ini")); // Engine/Config/NotForLicensees/Platform/Platform* ini yield return(FileReference.Combine(ProjectDir, "Config", "NotForLicensees", PlatformName, PlatformName + BaseIniName + ".ini")); // Engine/Config/NoRedist/Platform/Platform* ini yield return(FileReference.Combine(ProjectDir, "Config", "NoRedist", PlatformName, PlatformName + BaseIniName + ".ini")); } } DirectoryReference UserSettingsFolder = Utils.GetUserSettingDirectory(); // Match FPlatformProcess::UserSettingsDir() if (UserSettingsFolder != null) { // <AppData>/UE4/EngineConfig/User* ini yield return(FileReference.Combine(UserSettingsFolder, "Unreal Engine", "Engine", "Config", "User" + BaseIniName + ".ini")); } // Some user accounts (eg. SYSTEM on Windows) don't have a home directory. Ignore them if Environment.GetFolderPath() returns an empty string. string PersonalFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal); if (!String.IsNullOrEmpty(PersonalFolder)) { DirectoryReference PersonalConfigFolder; // Match FPlatformProcess::UserDir() if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac) { PersonalConfigFolder = DirectoryReference.Combine(new DirectoryReference(PersonalFolder), "Documents"); } else if (Environment.OSVersion.Platform == PlatformID.Unix) { PersonalConfigFolder = DirectoryReference.Combine(new DirectoryReference(PersonalFolder), "Documents"); } else { PersonalConfigFolder = new DirectoryReference(PersonalFolder); } // <Documents>/UE4/EngineConfig/User* ini yield return(FileReference.Combine(PersonalConfigFolder, "Unreal Engine", "Engine", "Config", "User" + BaseIniName + ".ini")); } // Game/Config/User* ini if (ProjectDir != null) { yield return(FileReference.Combine(ProjectDir, "Config", "User" + BaseIniName + ".ini")); } }
private ConfigHierarchy GetConfigCacheIni(ConfigHierarchyType Type) { return(ConfigCache.ReadHierarchy(Type, DirectoryReference.FromFile(ProjectFile), UnrealTargetPlatform.Lumin)); }
/// <summary> /// Get a ConfigHierarchy of the type you want with platform-specific config for the given Role. This object can be used to read .ini config values. /// Default params return the client platform's game config. /// This looks for the workspace files for the project that you are trying to run your test on. The config directory for that project must exist to find valid results. /// </summary> public static ConfigHierarchy GetConfigHierarchy(UnrealTestContext TestContext, ConfigHierarchyType ConfigType = ConfigHierarchyType.Game, UnrealTargetRole TargetRole = UnrealTargetRole.Client) { string ProjectPath = Path.Combine(Environment.CurrentDirectory, TestContext.BuildInfo.ProjectName); if (!Directory.Exists(ProjectPath)) { Log.Warning(string.Format("Directory does not exist at {0}! Returned ConfigHierarchy will not contain any config values. Make sure to sync the config directory for the project you are trying to run.", ProjectPath)); } return(ConfigCache.ReadHierarchy(ConfigType, new DirectoryReference(ProjectPath), TestContext.GetRoleContext(TargetRole).Platform)); }
/// <summary> /// Constructor /// </summary> /// <param name="Type">The hierarchy type</param> /// <param name="ProjectDir">The project directory to read from</param> /// <param name="Platform">Which platform-specific files to read</param> public ConfigHierarchyKey(ConfigHierarchyType Type, DirectoryReference ProjectDir, UnrealTargetPlatform Platform) { this.Type = Type; this.ProjectDir = ProjectDir; this.Platform = Platform; }
private ConfigHierarchy GetConfigCacheIni(ConfigHierarchyType Type) { // @todo Lumin: So - this is the problem with subclassing a platform currently - ini files. Lumin will use Android ini files // until I finish and get code over from another branch (key letter Q) that allows for insertion of a subclassed ini platform thing return(ConfigCache.ReadHierarchy(Type, DirectoryReference.FromFile(ProjectFile), UnrealTargetPlatform.Android)); }
/// <summary> /// Constructor /// </summary> /// <param name="ConfigType">Type of the config hierarchy to read from</param> /// <param name="SectionName">Section containing the setting</param> /// <param name="KeyName">Key name to search for. Optional; uses the name of the field if not set.</param> public ConfigFileAttribute(ConfigHierarchyType ConfigType, string SectionName, string KeyName = null) { this.ConfigType = ConfigType; this.SectionName = SectionName; this.KeyName = KeyName; }
/// <summary> /// Returns a list of INI filenames for the given project /// </summary> public static IEnumerable<FileReference> EnumerateConfigFileLocations(ConfigHierarchyType Type, DirectoryReference ProjectDir, UnrealTargetPlatform Platform) { string BaseIniName = Enum.GetName(typeof(ConfigHierarchyType), Type); string PlatformName = GetIniPlatformName(Platform); // cache some platform extension information that can be used inside the loops string PlatformExtensionEngineConfigDir = DirectoryReference.Combine(UnrealBuildTool.PlatformExtensionsDirectory, Platform.ToString(), "Engine").FullName; string PlatformExtensionProjectConfigDir = ProjectDir != null ? DirectoryReference.Combine(UnrealBuildTool.PlatformExtensionsDirectory, Platform.ToString(), ProjectDir.GetDirectoryName()).FullName : null; bool bHasPlatformExtensionEngineConfigDir = Directory.Exists(PlatformExtensionEngineConfigDir); bool bHasPlatformExtensionProjectConfigDir = PlatformExtensionProjectConfigDir != null && Directory.Exists(PlatformExtensionProjectConfigDir); foreach (ConfigLayer Layer in ConfigLayers) { bool bHasPlatformTag = Layer.Path.Contains("{PLATFORM}"); bool bHasProjectTag = Layer.Path.Contains("{PROJECT}"); bool bHasExpansionTag = Layer.Path.Contains("{ED}") || Layer.Path.Contains("{EF}"); bool bHasUserTag = Layer.Path.Contains("{USER}"); // skip platform layers if we are "platform-less", or user layers without a user dir if (bHasPlatformTag && Platform == null || bHasProjectTag && ProjectDir == null || bHasUserTag && GetUserDir() == null) { continue; } // basic replacements string LayerPath; // you can only have PROJECT or ENGINE, not both if (bHasProjectTag) { if (bHasPlatformTag && bHasPlatformExtensionProjectConfigDir) { LayerPath = Layer.ExtProjectPath.Replace("{EXTPROJECT}", PlatformExtensionProjectConfigDir); } else { LayerPath = Layer.Path.Replace("{PROJECT}", ProjectDir.FullName); } } else { if (bHasPlatformTag && bHasPlatformExtensionEngineConfigDir) { LayerPath = Layer.ExtEnginePath.Replace("{EXTENGINE}", PlatformExtensionEngineConfigDir); } else { LayerPath = Layer.Path.Replace("{ENGINE}", UnrealBuildTool.EngineDirectory.FullName); } } LayerPath = LayerPath.Replace("{TYPE}", BaseIniName); LayerPath = LayerPath.Replace("{USERSETTINGS}", Utils.GetUserSettingDirectory().FullName); if (bHasUserTag) LayerPath = LayerPath.Replace("{USER}", GetUserDir()); // handle expansion (and platform - the C++ code will validate that only expansion layers have platforms) if (bHasExpansionTag) { foreach (ConfigLayerExpansion Expansion in ConfigLayerExpansions) { // expansion replacements string ExpansionPath = LayerPath.Replace("{ED}", Expansion.DirectoryPrefix); ExpansionPath = ExpansionPath.Replace("{EF}", Expansion.FilePrefix); // now go up the ini parent chain if (bHasPlatformTag) { DataDrivenPlatformInfo.ConfigDataDrivenPlatformInfo Info = DataDrivenPlatformInfo.GetDataDrivenInfoForPlatform(PlatformName); if (Info != null && Info.IniParentChain != null) { // the IniParentChain foreach (string ParentPlatform in Info.IniParentChain) { yield return new FileReference(ExpansionPath.Replace("{PLATFORM}", ParentPlatform)); } } // always yield the active platform last yield return new FileReference(ExpansionPath.Replace("{PLATFORM}", PlatformName)); } else { yield return new FileReference(ExpansionPath); } } } else { yield return new FileReference(LayerPath); } } // Get the generated config file too. EditorSettings overrides this from if(Type == ConfigHierarchyType.EditorSettings) { yield return FileReference.Combine(GetGameAgnosticSavedDir(), "Config", PlatformName, BaseIniName + ".ini"); } else { yield return FileReference.Combine(GetGeneratedConfigDir(ProjectDir), PlatformName, BaseIniName + ".ini"); } }
/// <summary> /// Adds a new configuration value /// </summary> /// <param name="Type">The config hierarchy type</param> /// <param name="ProjectDir">The project directory</param> /// <param name="Platform">The platform being built</param> /// <param name="SectionName">Name of the config file section</param> /// <param name="KeyName">Name of the config file key</param> /// <param name="Values">Current values for this key</param> public void Add(ConfigHierarchyType Type, DirectoryReference ProjectDir, UnrealTargetPlatform Platform, string SectionName, string KeyName, IReadOnlyList <string> Values) { ConfigDependencyKey Key = new ConfigDependencyKey(Type, ProjectDir, Platform, SectionName, KeyName); Dependencies[Key] = Values; }
/// <summary> /// Returns a list of INI filenames for the given project /// </summary> public static IEnumerable <FileReference> EnumerateGeneratedConfigFileLocations(ConfigHierarchyType Type, DirectoryReference ProjectDir, UnrealTargetPlatform Platform) { string BaseIniName = Enum.GetName(typeof(ConfigHierarchyType), Type); string PlatformName = GetIniPlatformName(Platform); // Get the generated config file too. EditorSettings overrides this from if (Type == ConfigHierarchyType.EditorSettings) { yield return(FileReference.Combine(GetGameAgnosticSavedDir(), "Config", PlatformName, BaseIniName + ".ini")); } else { yield return(FileReference.Combine(GetGeneratedConfigDir(ProjectDir), PlatformName, BaseIniName + ".ini")); } }
/// <summary> /// Returns a list of INI filenames for the given project /// </summary> public static IEnumerable <FileReference> EnumerateConfigFileLocations(ConfigHierarchyType Type, DirectoryReference ProjectDir, UnrealTargetPlatform Platform) { string BaseIniName = Enum.GetName(typeof(ConfigHierarchyType), Type); string PlatformName = GetIniPlatformName(Platform); foreach (ConfigLayer Layer in ConfigLayers) { bool bHasPlatformTag, bHasProjectTag, bHasExpansionTag; string LayerPath = GetLayerPath(Layer, Platform.ToString(), PlatformName, ProjectDir, BaseIniName, out bHasPlatformTag, out bHasProjectTag, out bHasExpansionTag); // skip the layer if we aren't going to use it if (LayerPath == null) { continue; } // handle expansion (and platform - the C++ code will validate that only expansion layers have platforms) if (bHasExpansionTag) { foreach (ConfigLayerExpansion Expansion in ConfigLayerExpansions) { // expansion replacements string ExpansionPath = GetExpansionPath(Expansion, LayerPath); // now go up the ini parent chain if (bHasPlatformTag) { DataDrivenPlatformInfo.ConfigDataDrivenPlatformInfo Info = DataDrivenPlatformInfo.GetDataDrivenInfoForPlatform(PlatformName); if (Info != null && Info.IniParentChain != null) { // the IniParentChain foreach (string ParentPlatform in Info.IniParentChain) { // @note: We are using the ParentPlatform as both PlatformExtensionName _and_ IniPlatformName. This is because the parent // may not even exist as a UnrealTargetPlatform, and all we have is a string to look up, and it would just get the same // string back, if we did look it up. This could become an issue if Win64 becomes a PlatformExtension, and wants to have // a parent Platform, of ... something. This is likely to never be an issue, but leaving this note here just in case. string LocalLayerPath = GetLayerPath(Layer, ParentPlatform, ParentPlatform, ProjectDir, BaseIniName, out bHasPlatformTag, out bHasProjectTag, out bHasExpansionTag); string LocalExpansionPath = GetExpansionPath(Expansion, LocalLayerPath); yield return(new FileReference(LocalExpansionPath.Replace("{PLATFORM}", ParentPlatform))); } } // always yield the active platform last yield return(new FileReference(ExpansionPath.Replace("{PLATFORM}", PlatformName))); } else { yield return(new FileReference(ExpansionPath)); } } } else { yield return(new FileReference(LayerPath)); } } // Find all the generated config files foreach (FileReference GeneratedConfigFile in EnumerateGeneratedConfigFileLocations(Type, ProjectDir, Platform)) { yield return(GeneratedConfigFile); } }
/// <summary> /// Returns a list of INI filenames for the given project /// </summary> public static IEnumerable <FileReference> EnumerateConfigFileLocations(ConfigHierarchyType Type, DirectoryReference ProjectDir, UnrealTargetPlatform Platform) { string BaseIniName = Enum.GetName(typeof(ConfigHierarchyType), Type); string PlatformName = GetIniPlatformName(Platform); foreach (string Layer in ConfigLayers) { bool bHasPlatformTag = Layer.Contains("{PLATFORM}"); bool bHasProjectTag = Layer.Contains("{PROJECT}"); bool bHasUserTag = Layer.Contains("{USER}"); // skip certain layers if we are platform-less, project-less, or userdir-less if ((bHasPlatformTag && PlatformName == "None") || (bHasProjectTag && ProjectDir == null) || (bHasUserTag && GetUserDir() == null)) { continue; } string LayerPath = PerformBasicReplacements(Layer, BaseIniName); // we only expand engine/project inis if (Layer.Contains("{ENGINE}") || Layer.Contains("{PROJECT}")) { foreach (ConfigLayerExpansion Expansion in ConfigLayerExpansions) { // expansion replacements string ExpandedPath = PerformExpansionReplacements(Expansion, LayerPath); // if nothing was replaced, then skip it, as it won't change anything if (ExpandedPath == null) { continue; } // now go up the ini parent chain if (bHasPlatformTag) { DataDrivenPlatformInfo.ConfigDataDrivenPlatformInfo Info = DataDrivenPlatformInfo.GetDataDrivenInfoForPlatform(PlatformName); if (Info != null && Info.IniParentChain != null) { // the IniParentChain foreach (string ParentPlatform in Info.IniParentChain) { // @note: We are using the ParentPlatform as both PlatformExtensionName _and_ IniPlatformName. This is because the parent // may not even exist as a UnrealTargetPlatform, and all we have is a string to look up, and it would just get the same // string back, if we did look it up. This could become an issue if Win64 becomes a PlatformExtension, and wants to have // a parent Platform, of ... something. This is likely to never be an issue, but leaving this note here just in case. yield return(new FileReference(PerformFinalExpansions(ExpandedPath, ParentPlatform, ProjectDir))); } } // always yield the active platform last yield return(new FileReference(PerformFinalExpansions(ExpandedPath, PlatformName, ProjectDir))); } else { yield return(new FileReference(PerformFinalExpansions(ExpandedPath, "", ProjectDir))); } } } else { yield return(new FileReference(LayerPath)); } } // Find all the generated config files foreach (FileReference GeneratedConfigFile in EnumerateGeneratedConfigFileLocations(Type, ProjectDir, Platform)) { yield return(GeneratedConfigFile); } }