static List <BuildProduct> GetBuildProductsFromReceipts(DirectoryReference EngineDir, DirectoryReference ProjectDir, List <FileReference> ReceiptFileNames) { List <BuildProduct> BuildProducts = new List <BuildProduct>(); foreach (FileReference ReceiptFileName in ReceiptFileNames) { TargetReceipt Receipt; if (!TargetReceipt.TryRead(ReceiptFileName, EngineDir, ProjectDir, out Receipt)) { throw new AutomationException("Missing or invalid target receipt ({0})", ReceiptFileName); } BuildProducts.AddRange(Receipt.BuildProducts); } return(BuildProducts); }
FileReference[] CompilePlugin(FileReference HostProjectFile, FileReference HostProjectPluginFile, PluginDescriptor Plugin, List <UnrealTargetPlatform> HostPlatforms, List <UnrealTargetPlatform> TargetPlatforms, string AdditionalArgs) { List <FileReference> ReceiptFileNames = new List <FileReference>(); // Build the host platforms if (HostPlatforms.Count > 0) { CommandUtils.Log("Building plugin for host platforms: {0}", String.Join(", ", HostPlatforms)); foreach (UnrealTargetPlatform HostPlatform in HostPlatforms) { if (Plugin.bCanBeUsedWithUnrealHeaderTool) { CompilePluginWithUBT(null, HostProjectPluginFile, Plugin, "UnrealHeaderTool", TargetType.Program, HostPlatform, UnrealTargetConfiguration.Development, ReceiptFileNames, String.Format("{0} -plugin={1}", AdditionalArgs, CommandUtils.MakePathSafeToUseWithCommandLine(HostProjectPluginFile.FullName))); } CompilePluginWithUBT(HostProjectFile, HostProjectPluginFile, Plugin, "UE4Editor", TargetType.Editor, HostPlatform, UnrealTargetConfiguration.Development, ReceiptFileNames, AdditionalArgs); } } // Add the game targets if (TargetPlatforms.Count > 0) { CommandUtils.Log("Building plugin for target platforms: {0}", String.Join(", ", TargetPlatforms)); foreach (UnrealTargetPlatform TargetPlatform in TargetPlatforms) { if (Plugin.SupportsTargetPlatform(TargetPlatform)) { CompilePluginWithUBT(HostProjectFile, HostProjectPluginFile, Plugin, "UE4Game", TargetType.Game, TargetPlatform, UnrealTargetConfiguration.Development, ReceiptFileNames, null); CompilePluginWithUBT(HostProjectFile, HostProjectPluginFile, Plugin, "UE4Game", TargetType.Game, TargetPlatform, UnrealTargetConfiguration.Shipping, ReceiptFileNames, null); } } } // Package the plugin to the output folder HashSet <FileReference> BuildProducts = new HashSet <FileReference>(); foreach (FileReference ReceiptFileName in ReceiptFileNames) { TargetReceipt Receipt; if (!TargetReceipt.TryRead(ReceiptFileName, CommandUtils.EngineDirectory, HostProjectFile.Directory, out Receipt)) { throw new AutomationException("Missing or invalid target receipt ({0})", ReceiptFileName); } BuildProducts.UnionWith(Receipt.BuildProducts.Select(x => x.Path).Where(x => x.IsUnderDirectory(HostProjectPluginFile.Directory))); BuildProducts.UnionWith(Receipt.PrecompiledBuildDependencies.Where(x => x.IsUnderDirectory(HostProjectPluginFile.Directory))); BuildProducts.UnionWith(Receipt.PrecompiledRuntimeDependencies.Where(x => x.IsUnderDirectory(HostProjectPluginFile.Directory))); } return(BuildProducts.ToArray()); }
/// <summary> /// Execute the task. /// </summary> /// <param name="Job">Information about the current job</param> /// <param name="BuildProducts">Set of build products produced by this node.</param> /// <param name="TagNameToFileSet">Mapping from tag names to the set of files they include</param> /// <returns>True if the task succeeded</returns> public override bool Execute(JobContext Job, HashSet <FileReference> BuildProducts, Dictionary <string, HashSet <FileReference> > TagNameToFileSet) { // Get the project path, and check it exists FileReference ProjectFile = null; if (Parameters.Project != null) { ProjectFile = ResolveFile(Parameters.Project); if (!ProjectFile.Exists()) { CommandUtils.LogError("Couldn't find project '{0}'", ProjectFile.FullName); return(false); } } // Get the directories used for staging this project DirectoryReference SourceEngineDir = UnrealBuildTool.UnrealBuildTool.EngineDirectory; DirectoryReference SourceProjectDir = (ProjectFile == null)? SourceEngineDir : ProjectFile.Directory; // Get the output directories. We flatten the directory structure on output. DirectoryReference TargetDir = ResolveDirectory(Parameters.ToDir); DirectoryReference TargetEngineDir = DirectoryReference.Combine(TargetDir, "Engine"); DirectoryReference TargetProjectDir = DirectoryReference.Combine(TargetDir, ProjectFile.GetFileNameWithoutExtension()); // Get the path to the receipt string ReceiptFileName = TargetReceipt.GetDefaultPath(SourceProjectDir.FullName, Parameters.Target, Parameters.Platform, Parameters.Configuration, Parameters.Architecture); // Try to load it TargetReceipt Receipt; if (!TargetReceipt.TryRead(ReceiptFileName, out Receipt)) { CommandUtils.LogError("Couldn't read receipt '{0}'", ReceiptFileName); return(false); } // Expand all the paths from the receipt Receipt.ExpandPathVariables(SourceEngineDir, SourceProjectDir); // Stage all the build products needed at runtime HashSet <FileReference> SourceFiles = new HashSet <FileReference>(); foreach (BuildProduct BuildProduct in Receipt.BuildProducts.Where(x => x.Type != BuildProductType.StaticLibrary && x.Type != BuildProductType.ImportLibrary)) { SourceFiles.Add(new FileReference(BuildProduct.Path)); } foreach (RuntimeDependency RuntimeDependency in Receipt.RuntimeDependencies.Where(x => x.Type != StagedFileType.UFS)) { SourceFiles.UnionWith(CommandUtils.ResolveFilespec(CommandUtils.RootDirectory, RuntimeDependency.Path, new string[] { ".../*.umap", ".../*.uasset" })); } // Get all the target files List <FileReference> TargetFiles = new List <FileReference>(); foreach (FileReference SourceFile in SourceFiles) { // Get the destination file to copy to, mapping to the new engine and project directories as appropriate FileReference TargetFile; if (SourceFile.IsUnderDirectory(SourceEngineDir)) { TargetFile = FileReference.Combine(TargetEngineDir, SourceFile.MakeRelativeTo(SourceEngineDir)); } else { TargetFile = FileReference.Combine(TargetProjectDir, SourceFile.MakeRelativeTo(SourceProjectDir)); } // Fixup the case of the output file. Would expect Platform.DeployLowerCaseFilenames() to return true here, but seems not to be the case. if (Parameters.Platform == UnrealTargetPlatform.PS4) { TargetFile = FileReference.Combine(TargetDir, TargetFile.MakeRelativeTo(TargetDir).ToLowerInvariant()); } // Only copy the output file if it doesn't already exist. We can stage multiple targets to the same output directory. if (Parameters.Overwrite || !TargetFile.Exists()) { TargetFile.Directory.CreateDirectory(); CommandUtils.CopyFile(SourceFile.FullName, TargetFile.FullName); } // Add it to the list of target files TargetFiles.Add(TargetFile); } // Apply the optional tag to the build products foreach (string TagName in FindTagNamesFromList(Parameters.Tag)) { FindOrAddTagSet(TagNameToFileSet, TagName).UnionWith(TargetFiles); } // Add the target file to the list of build products BuildProducts.UnionWith(TargetFiles); return(true); }
/// <summary> /// Execute the task. /// </summary> /// <param name="Job">Information about the current job</param> /// <param name="BuildProducts">Set of build products produced by this node.</param> /// <param name="TagNameToFileSet">Mapping from tag names to the set of files they include</param> /// <returns>True if the task succeeded</returns> public override bool Execute(JobContext Job, HashSet <FileReference> BuildProducts, Dictionary <string, HashSet <FileReference> > TagNameToFileSet) { // Set the Engine directory DirectoryReference EngineDir = DirectoryReference.Combine(CommandUtils.RootDirectory, "Engine"); if (!String.IsNullOrEmpty(Parameters.EngineDir)) { EngineDir = DirectoryReference.Combine(CommandUtils.RootDirectory, Parameters.EngineDir); } // Set the Project directory DirectoryReference ProjectDir = DirectoryReference.Combine(CommandUtils.RootDirectory, "Engine"); if (!String.IsNullOrEmpty(Parameters.ProjectDir)) { ProjectDir = DirectoryReference.Combine(CommandUtils.RootDirectory, Parameters.ProjectDir); } // Resolve the input list IEnumerable <FileReference> TargetFiles = ResolveFilespec(CommandUtils.RootDirectory, Parameters.Files, TagNameToFileSet); HashSet <FileReference> Files = new HashSet <FileReference>(); foreach (FileReference TargetFile in TargetFiles) { // check all files are .target files if (TargetFile.GetExtension() != ".target") { CommandUtils.LogError("Invalid file passed to TagReceipt task ({0})", TargetFile.FullName); continue; } // Read the receipt TargetReceipt Receipt; if (!TargetReceipt.TryRead(TargetFile, EngineDir, ProjectDir, out Receipt)) { CommandUtils.LogWarning("Unable to load file using TagReceipt task ({0})", TargetFile.FullName); continue; } if (Parameters.BuildProducts) { foreach (BuildProduct BuildProduct in Receipt.BuildProducts) { if (String.IsNullOrEmpty(Parameters.BuildProductType) || BuildProduct.Type == BuildProductType) { Files.Add(BuildProduct.Path); } } } if (Parameters.RuntimeDependencies) { foreach (RuntimeDependency RuntimeDependency in Receipt.RuntimeDependencies) { if (String.IsNullOrEmpty(Parameters.StagedFileType) || RuntimeDependency.Type == StagedFileType) { // Only add files that exist as dependencies are assumed to always exist FileReference DependencyPath = RuntimeDependency.Path; if (FileReference.Exists(DependencyPath)) { Files.Add(DependencyPath); } else { CommandUtils.LogWarning("File listed as RuntimeDependency in {0} does not exist ({1})", TargetFile.FullName, DependencyPath.FullName); } } } } if (Parameters.PrecompiledBuildDependencies) { foreach (FileReference PrecompiledBuildDependency in Receipt.PrecompiledBuildDependencies) { // Only add files that exist as dependencies are assumed to always exist FileReference DependencyPath = PrecompiledBuildDependency; if (FileReference.Exists(DependencyPath)) { Files.Add(DependencyPath); } else { CommandUtils.LogWarning("File listed as PrecompiledBuildDependency in {0} does not exist ({1})", TargetFile.FullName, DependencyPath.FullName); } } } if (Parameters.PrecompiledRuntimeDependencies) { foreach (FileReference PrecompiledRuntimeDependency in Receipt.PrecompiledRuntimeDependencies) { // Only add files that exist as dependencies are assumed to always exist FileReference DependencyPath = PrecompiledRuntimeDependency; if (FileReference.Exists(DependencyPath)) { Files.Add(DependencyPath); } else { CommandUtils.LogWarning("File listed as PrecompiledRuntimeDependency in {0} does not exist ({1})", TargetFile.FullName, DependencyPath.FullName); } } } } // Apply the tag to all the matching files FindOrAddTagSet(TagNameToFileSet, Parameters.With).UnionWith(Files); return(true); }
/// <summary> /// Execute the task. /// </summary> /// <param name="Job">Information about the current job</param> /// <param name="BuildProducts">Set of build products produced by this node.</param> /// <param name="TagNameToFileSet">Mapping from tag names to the set of files they include</param> public override void Execute(JobContext Job, HashSet <FileReference> BuildProducts, Dictionary <string, HashSet <FileReference> > TagNameToFileSet) { // Set the Engine directory DirectoryReference EngineDir = Parameters.EngineDir ?? CommandUtils.EngineDirectory; // Resolve the input list IEnumerable <FileReference> TargetFiles = ResolveFilespec(CommandUtils.RootDirectory, Parameters.Files, TagNameToFileSet); foreach (FileReference TargetFile in TargetFiles) { // check all files are .target files if (TargetFile.GetExtension() != ".target") { throw new AutomationException("Invalid file passed to TagReceipt task ({0})", TargetFile.FullName); } // Print the name of the file being scanned Log.TraceInformation("Sanitizing {0}", TargetFile); using (new LogIndentScope(" ")) { // Read the receipt TargetReceipt Receipt; if (!TargetReceipt.TryRead(TargetFile, EngineDir, out Receipt)) { CommandUtils.LogWarning("Unable to load file using TagReceipt task ({0})", TargetFile.FullName); continue; } // Remove any build products that don't exist List <BuildProduct> NewBuildProducts = new List <BuildProduct>(Receipt.BuildProducts.Count); foreach (BuildProduct BuildProduct in Receipt.BuildProducts) { if (FileReference.Exists(BuildProduct.Path)) { NewBuildProducts.Add(BuildProduct); } else { Log.TraceInformation("Removing build product: {0}", BuildProduct.Path); } } Receipt.BuildProducts = NewBuildProducts; // Remove any runtime dependencies that don't exist RuntimeDependencyList NewRuntimeDependencies = new RuntimeDependencyList(); foreach (RuntimeDependency RuntimeDependency in Receipt.RuntimeDependencies) { if (FileReference.Exists(RuntimeDependency.Path)) { NewRuntimeDependencies.Add(RuntimeDependency); } else { Log.TraceInformation("Removing runtime dependency: {0}", RuntimeDependency.Path); } } Receipt.RuntimeDependencies = NewRuntimeDependencies; // Save the new receipt Receipt.Write(TargetFile, EngineDir); } } }
/// <summary> /// Execute the task. /// </summary> /// <param name="Job">Information about the current job</param> /// <param name="BuildProducts">Set of build products produced by this node.</param> /// <param name="TagNameToFileSet">Mapping from tag names to the set of files they include</param> public override void Execute(JobContext Job, HashSet <FileReference> BuildProducts, Dictionary <string, HashSet <FileReference> > TagNameToFileSet) { // Output a warning if the project directory is specified if (Parameters.ProjectDir != null) { CommandUtils.LogWarning("The ProjectDir argument to the TagReceipt parameter is deprecated. This path is now determined automatically from the receipt."); } // Set the Engine directory DirectoryReference EngineDir = Parameters.EngineDir ?? CommandUtils.EngineDirectory; // Resolve the input list IEnumerable <FileReference> TargetFiles = ResolveFilespec(CommandUtils.RootDirectory, Parameters.Files, TagNameToFileSet); HashSet <FileReference> Files = new HashSet <FileReference>(); foreach (FileReference TargetFile in TargetFiles) { // check all files are .target files if (TargetFile.GetExtension() != ".target") { throw new AutomationException("Invalid file passed to TagReceipt task ({0})", TargetFile.FullName); } // Read the receipt TargetReceipt Receipt; if (!TargetReceipt.TryRead(TargetFile, EngineDir, out Receipt)) { CommandUtils.LogWarning("Unable to load file using TagReceipt task ({0})", TargetFile.FullName); continue; } if (Parameters.BuildProducts) { foreach (BuildProduct BuildProduct in Receipt.BuildProducts) { if (BuildProductType.HasValue && BuildProduct.Type != BuildProductType.Value) { continue; } if (StagedFileType.HasValue && TargetReceipt.GetStageTypeFromBuildProductType(BuildProduct) != StagedFileType.Value) { continue; } Files.Add(BuildProduct.Path); } } if (Parameters.RuntimeDependencies) { foreach (RuntimeDependency RuntimeDependency in Receipt.RuntimeDependencies) { // Skip anything that doesn't match the files we want if (BuildProductType.HasValue) { continue; } if (StagedFileType.HasValue && RuntimeDependency.Type != StagedFileType.Value) { continue; } // Check which files exist, and warn about any that don't. Ignore debug files, as they are frequently excluded for size (eg. UE4 on GitHub). This matches logic during staging. FileReference DependencyPath = RuntimeDependency.Path; if (FileReference.Exists(DependencyPath)) { Files.Add(DependencyPath); } else if (RuntimeDependency.Type != UnrealBuildTool.StagedFileType.DebugNonUFS) { CommandUtils.LogWarning("File listed as RuntimeDependency in {0} does not exist ({1})", TargetFile.FullName, DependencyPath.FullName); } } } } // Apply the tag to all the matching files FindOrAddTagSet(TagNameToFileSet, Parameters.With).UnionWith(Files); }
/// <summary> /// Execute the task. /// </summary> /// <param name="Job">Information about the current job</param> /// <param name="BuildProducts">Set of build products produced by this node.</param> /// <param name="TagNameToFileSet">Mapping from tag names to the set of files they include</param> public override void Execute(JobContext Job, HashSet <FileReference> BuildProducts, Dictionary <string, HashSet <FileReference> > TagNameToFileSet) { // Get the project path, and check it exists FileReference ProjectFile = Parameters.Project; if (Parameters.Project != null && !FileReference.Exists(ProjectFile)) { throw new AutomationException("Couldn't find project '{0}'", ProjectFile.FullName); } // Get the directories used for staging this project DirectoryReference SourceEngineDir = CommandUtils.EngineDirectory; DirectoryReference SourceProjectDir = (ProjectFile == null)? SourceEngineDir : ProjectFile.Directory; // Get the output directories. We flatten the directory structure on output. DirectoryReference TargetDir = Parameters.ToDir; DirectoryReference TargetEngineDir = DirectoryReference.Combine(TargetDir, "Engine"); DirectoryReference TargetProjectDir = DirectoryReference.Combine(TargetDir, ProjectFile.GetFileNameWithoutExtension()); // Get the path to the receipt FileReference ReceiptFileName = TargetReceipt.GetDefaultPath(SourceProjectDir, Parameters.Target, Parameters.Platform, Parameters.Configuration, Parameters.Architecture); // Try to load it TargetReceipt Receipt; if (!TargetReceipt.TryRead(ReceiptFileName, out Receipt)) { throw new AutomationException("Couldn't read receipt '{0}'", ReceiptFileName); } // Stage all the build products needed at runtime HashSet <FileReference> SourceFiles = new HashSet <FileReference>(); foreach (BuildProduct BuildProduct in Receipt.BuildProducts) { SourceFiles.Add(BuildProduct.Path); } foreach (RuntimeDependency RuntimeDependency in Receipt.RuntimeDependencies.Where(x => x.Type != StagedFileType.UFS)) { SourceFiles.Add(RuntimeDependency.Path); } // Get all the target files List <FileReference> TargetFiles = new List <FileReference>(); foreach (FileReference SourceFile in SourceFiles) { // Get the destination file to copy to, mapping to the new engine and project directories as appropriate FileReference TargetFile; if (SourceFile.IsUnderDirectory(SourceEngineDir)) { TargetFile = FileReference.Combine(TargetEngineDir, SourceFile.MakeRelativeTo(SourceEngineDir)); } else { TargetFile = FileReference.Combine(TargetProjectDir, SourceFile.MakeRelativeTo(SourceProjectDir)); } // Fixup the case of the output file. Would expect Platform.DeployLowerCaseFilenames() to return true here, but seems not to be the case. if (Parameters.Platform == UnrealTargetPlatform.PS4) { TargetFile = FileReference.Combine(TargetDir, TargetFile.MakeRelativeTo(TargetDir).ToLowerInvariant()); } // Only copy the output file if it doesn't already exist. We can stage multiple targets to the same output directory. if (Parameters.Overwrite || !FileReference.Exists(TargetFile)) { DirectoryReference.CreateDirectory(TargetFile.Directory); CommandUtils.CopyFile(SourceFile.FullName, TargetFile.FullName); // Force all destination files to not readonly. CommandUtils.SetFileAttributes(TargetFile.FullName, ReadOnly: false); } // Add it to the list of target files TargetFiles.Add(TargetFile); } // Apply the optional tag to the build products foreach (string TagName in FindTagNamesFromList(Parameters.Tag)) { FindOrAddTagSet(TagNameToFileSet, TagName).UnionWith(TargetFiles); } // Add the target file to the list of build products BuildProducts.UnionWith(TargetFiles); }
/// <summary> /// Execute the task. /// </summary> /// <param name="Job">Information about the current job</param> /// <param name="BuildProducts">Set of build products produced by this node.</param> /// <param name="TagNameToFileSet">Mapping from tag names to the set of files they include</param> /// <returns>True if the task succeeded</returns> public override bool Execute(JobContext Job, HashSet <FileReference> BuildProducts, Dictionary <string, HashSet <FileReference> > TagNameToFileSet) { // Set the Engine directory DirectoryReference EngineDir = DirectoryReference.Combine(CommandUtils.RootDirectory, "Engine"); if (!String.IsNullOrEmpty(Parameters.EngineDir)) { EngineDir = DirectoryReference.Combine(CommandUtils.RootDirectory, Parameters.EngineDir); } // Set the Project directory DirectoryReference ProjectDir = DirectoryReference.Combine(CommandUtils.RootDirectory, "Engine"); if (!String.IsNullOrEmpty(Parameters.ProjectDir)) { ProjectDir = DirectoryReference.Combine(CommandUtils.RootDirectory, Parameters.ProjectDir); } // Resolve the input list IEnumerable <FileReference> TargetFiles = ResolveFilespec(CommandUtils.RootDirectory, Parameters.Files, TagNameToFileSet); HashSet <FileReference> Files = new HashSet <FileReference>(); HashSet <string> WildcardDependencies = new HashSet <string>(); foreach (FileReference TargetFile in TargetFiles) { // check all files are .target files if (TargetFile.GetExtension() != ".target") { CommandUtils.LogError("Invalid file passed to TagReceipt task ({0})", TargetFile.FullName); continue; } // Read the receipt TargetReceipt Receipt; if (!TargetReceipt.TryRead(TargetFile.FullName, out Receipt)) { CommandUtils.LogWarning("Unable to load file using TagReceipt task ({0})", TargetFile.FullName); continue; } // Convert the paths to absolute Receipt.ExpandPathVariables(EngineDir, ProjectDir); if (Parameters.BuildProducts) { foreach (BuildProduct BuildProduct in Receipt.BuildProducts) { if (String.IsNullOrEmpty(Parameters.BuildProductType) || BuildProduct.Type == BuildProductType) { Files.Add(new FileReference(BuildProduct.Path)); } } } if (Parameters.RuntimeDependencies) { foreach (RuntimeDependency RuntimeDependency in Receipt.RuntimeDependencies) { if (String.IsNullOrEmpty(Parameters.StagedFileType) || RuntimeDependency.Type == StagedFileType) { // If it doesn't contain any wildcards, just add the pattern directly if (FileFilter.FindWildcardIndex(RuntimeDependency.Path) == -1) { // Only add files that exist as dependencies are assumed to always exist FileReference DependencyPath = new FileReference(RuntimeDependency.Path); if (DependencyPath.Exists()) { Files.Add(DependencyPath); } else { // Give a warning about files that don't exist so that we can clean up build.cs files CommandUtils.LogWarning("File listed as RuntimeDependency in {0} does not exist ({1})", TargetFile.FullName, DependencyPath.FullName); } } else { WildcardDependencies.Add(RuntimeDependency.Path); } } } } if (Parameters.PrecompiledBuildDependencies) { foreach (string PrecompiledBuildDependency in Receipt.PrecompiledBuildDependencies) { // If it doesn't contain any wildcards, just add the pattern directly if (FileFilter.FindWildcardIndex(PrecompiledBuildDependency) == -1) { // Only add files that exist as dependencies are assumed to always exist FileReference DependencyPath = new FileReference(PrecompiledBuildDependency); if (DependencyPath.Exists()) { Files.Add(DependencyPath); } else { // Give a warning about files that don't exist so that we can clean up build.cs files CommandUtils.LogWarning("File listed as PrecompiledBuildDependency in {0} does not exist ({1})", TargetFile.FullName, DependencyPath.FullName); } } else { WildcardDependencies.Add(PrecompiledBuildDependency); } } } if (Parameters.PrecompiledRuntimeDependencies) { foreach (string PrecompiledRuntimeDependency in Receipt.PrecompiledRuntimeDependencies) { // If it doesn't contain any wildcards, just add the pattern directly if (FileFilter.FindWildcardIndex(PrecompiledRuntimeDependency) == -1) { // Only add files that exist as dependencies are assumed to always exist FileReference DependencyPath = new FileReference(PrecompiledRuntimeDependency); if (DependencyPath.Exists()) { Files.Add(DependencyPath); } else { // Give a warning about files that don't exist so that we can clean up build.cs files CommandUtils.LogWarning("File listed as PrecompiledRuntimeDependency in {0} does not exist ({1})", TargetFile.FullName, DependencyPath.FullName); } } else { WildcardDependencies.Add(PrecompiledRuntimeDependency); } } } } // Turn any wildcards into a file list Files.UnionWith(ResolveFilespecWithExcludePatterns(CommandUtils.RootDirectory, WildcardDependencies.ToList(), new List <string>(), TagNameToFileSet)); // Apply the tag to all the matching files FindOrAddTagSet(TagNameToFileSet, Parameters.With).UnionWith(Files); return(true); }