/// <summary> /// The main project directory and all found platform extension project directories /// </summary> public static DirectoryReference[] GetAllProjectDirectories(FileReference ProjectFile, string Suffix = "") { // project name may not match the directory name, so use the ProjectFile's name as ProjectName string ProjectName = ProjectFile.GetFileNameWithoutAnyExtensions(); List <DirectoryReference> ProjectDirectories = new List <DirectoryReference>() { DirectoryReference.Combine(ProjectFile.Directory, Suffix) }; if (DirectoryReference.Exists(PlatformExtensionsDirectory)) { foreach (DirectoryReference PlatformDirectory in DirectoryReference.EnumerateDirectories(PlatformExtensionsDirectory)) { DirectoryReference PlatformEngineDirectory = DirectoryReference.Combine(PlatformDirectory, ProjectName, Suffix); if (DirectoryReference.Exists(PlatformEngineDirectory)) { ProjectDirectories.Add(PlatformEngineDirectory); } } } return(ProjectDirectories.ToArray()); }
protected IOSProvisioningData(IOSProjectSettings ProjectSettings, bool bIsTVOS, bool bForDistribtion) { SigningCertificate = ProjectSettings.SigningCertificate; MobileProvision = ProjectSettings.MobileProvision; FileReference ProjectFile = ProjectSettings.ProjectFile; if (!string.IsNullOrEmpty(SigningCertificate)) { // verify the certificate Process IPPProcess = new Process(); if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac) { string IPPCmd = "\"" + UnrealBuildTool.EngineDirectory + "/Binaries/DotNET/IOS/IPhonePackager.exe\" certificates " + ((ProjectFile != null) ? ("\"" + ProjectFile.ToString() + "\"") : "Engine") + " -bundlename " + ProjectSettings.BundleIdentifier + (bForDistribtion ? " -distribution" : ""); IPPProcess.StartInfo.WorkingDirectory = UnrealBuildTool.EngineDirectory.ToString(); IPPProcess.StartInfo.FileName = UnrealBuildTool.EngineDirectory + "/Build/BatchFiles/Mac/RunMono.sh"; IPPProcess.StartInfo.Arguments = IPPCmd; IPPProcess.OutputDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); IPPProcess.ErrorDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); } else { string IPPCmd = "certificates " + ((ProjectFile != null) ? ("\"" + ProjectFile.ToString() + "\"") : "Engine") + " -bundlename " + ProjectSettings.BundleIdentifier + (bForDistribtion ? " -distribution" : ""); IPPProcess.StartInfo.WorkingDirectory = UnrealBuildTool.EngineDirectory.ToString(); IPPProcess.StartInfo.FileName = UnrealBuildTool.EngineDirectory + "\\Binaries\\DotNET\\IOS\\IPhonePackager.exe"; IPPProcess.StartInfo.Arguments = IPPCmd; IPPProcess.OutputDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); IPPProcess.ErrorDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); } Utils.RunLocalProcess(IPPProcess); } else { SigningCertificate = bForDistribtion ? "iPhone Distribution" : "iPhone Developer"; bHaveCertificate = true; } if (string.IsNullOrEmpty(MobileProvision) || // no provision specified !File.Exists((BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac ? (Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/") : (Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "/Apple Computer/MobileDevice/Provisioning Profiles/")) + MobileProvision) || // file doesn't exist !bHaveCertificate) // certificate doesn't exist { SigningCertificate = ""; MobileProvision = ""; Log.TraceLog("Provision not specified or not found for " + ((ProjectFile != null) ? ProjectFile.GetFileNameWithoutAnyExtensions() : "UE4Game") + ", searching for compatible match..."); Process IPPProcess = new Process(); if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac) { string IPPCmd = "\"" + UnrealBuildTool.EngineDirectory + "/Binaries/DotNET/IOS/IPhonePackager.exe\" signing_match " + ((ProjectFile != null) ? ("\"" + ProjectFile.ToString() + "\"") : "Engine") + " -bundlename " + ProjectSettings.BundleIdentifier + (bIsTVOS ? " -tvos" : "") + (bForDistribtion ? " -distribution" : ""); IPPProcess.StartInfo.WorkingDirectory = UnrealBuildTool.EngineDirectory.ToString(); IPPProcess.StartInfo.FileName = UnrealBuildTool.EngineDirectory + "/Build/BatchFiles/Mac/RunMono.sh"; IPPProcess.StartInfo.Arguments = IPPCmd; IPPProcess.OutputDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); IPPProcess.ErrorDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); } else { string IPPCmd = "signing_match " + ((ProjectFile != null) ? ("\"" + ProjectFile.ToString() + "\"") : "Engine") + " -bundlename " + ProjectSettings.BundleIdentifier + (bIsTVOS ? " -tvos" : "") + (bForDistribtion ? " -distribution" : ""); IPPProcess.StartInfo.WorkingDirectory = UnrealBuildTool.EngineDirectory.ToString(); IPPProcess.StartInfo.FileName = UnrealBuildTool.EngineDirectory + "\\Binaries\\DotNET\\IOS\\IPhonePackager.exe"; IPPProcess.StartInfo.Arguments = IPPCmd; IPPProcess.OutputDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); IPPProcess.ErrorDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); } Utils.RunLocalProcess(IPPProcess); Log.TraceLog("Provision found for " + ((ProjectFile != null) ? ProjectFile.GetFileNameWithoutAnyExtensions() : "UE4Game") + ", Provision: " + MobileProvision + " Certificate: " + SigningCertificate); } // add to the dictionary SigningCertificate = SigningCertificate.Replace("\"", ""); // read the provision to get the UUID string filename = (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac ? (Environment.GetEnvironmentVariable("HOME") + "/Library/MobileDevice/Provisioning Profiles/") : (Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "/Apple Computer/MobileDevice/Provisioning Profiles/")) + MobileProvision; if (File.Exists(filename)) { string AllText = File.ReadAllText(filename); int idx = AllText.IndexOf("<key>UUID</key>"); if (idx > 0) { idx = AllText.IndexOf("<string>", idx); if (idx > 0) { idx += "<string>".Length; MobileProvisionUUID = AllText.Substring(idx, AllText.IndexOf("</string>", idx) - idx); } } idx = AllText.IndexOf("<key>com.apple.developer.team-identifier</key>"); if (idx > 0) { idx = AllText.IndexOf("<string>", idx); if (idx > 0) { idx += "<string>".Length; TeamUUID = AllText.Substring(idx, AllText.IndexOf("</string>", idx) - idx); } } } else { Log.TraceLog("No matching provision file was discovered. Please ensure you have a compatible provision installed."); } }
/// <summary> /// Protected constructor. Used by TVOSProjectSettings. /// </summary> /// <param name="ProjectFile">The project file to read settings for</param> /// <param name="Platform">The platform to read settings for</param> protected IOSProjectSettings(FileReference ProjectFile, UnrealTargetPlatform Platform) { this.ProjectFile = ProjectFile; ConfigCache.ReadSettings(DirectoryReference.FromFile(ProjectFile), Platform, this); BundleIdentifier = BundleIdentifier.Replace("[PROJECT_NAME]", ((ProjectFile != null) ? ProjectFile.GetFileNameWithoutAnyExtensions() : "UE4Game")).Replace("_", ""); }
private static Assembly CompileAssembly(FileReference OutputAssemblyPath, List <FileReference> SourceFileNames, List <string> ReferencedAssembies, List <string> PreprocessorDefines = null, bool TreatWarningsAsErrors = false) { CSharpParseOptions ParseOptions = new CSharpParseOptions( languageVersion: LanguageVersion.Latest, kind: SourceCodeKind.Regular, preprocessorSymbols: PreprocessorDefines ); List <SyntaxTree> SyntaxTrees = new List <SyntaxTree>(); foreach (FileReference SourceFileName in SourceFileNames) { SourceText Source = SourceText.From(File.ReadAllText(SourceFileName.FullName)); SyntaxTree Tree = CSharpSyntaxTree.ParseText(Source, ParseOptions, SourceFileName.FullName); IEnumerable <Diagnostic> Diagnostics = Tree.GetDiagnostics(); if (Diagnostics.Count() > 0) { Log.TraceWarning($"Errors generated while parsing '{SourceFileName.FullName}'"); LogDiagnostics(Tree.GetDiagnostics()); return(null); } SyntaxTrees.Add(Tree); } // Create the output directory if it doesn't exist already DirectoryInfo DirInfo = new DirectoryInfo(OutputAssemblyPath.Directory.FullName); if (!DirInfo.Exists) { try { DirInfo.Create(); } catch (Exception Ex) { throw new BuildException(Ex, "Unable to create directory '{0}' for intermediate assemblies (Exception: {1})", OutputAssemblyPath, Ex.Message); } } List <MetadataReference> MetadataReferences = new List <MetadataReference>(); if (ReferencedAssembies != null) { foreach (string Reference in ReferencedAssembies) { MetadataReferences.Add(MetadataReference.CreateFromFile(Reference)); } } MetadataReferences.Add(MetadataReference.CreateFromFile(typeof(object).Assembly.Location)); MetadataReferences.Add(MetadataReference.CreateFromFile(Assembly.Load("System.Runtime").Location)); MetadataReferences.Add(MetadataReference.CreateFromFile(Assembly.Load("System.Collections").Location)); MetadataReferences.Add(MetadataReference.CreateFromFile(Assembly.Load("System.IO").Location)); MetadataReferences.Add(MetadataReference.CreateFromFile(Assembly.Load("System.IO.FileSystem").Location)); MetadataReferences.Add(MetadataReference.CreateFromFile(Assembly.Load("System.Console").Location)); MetadataReferences.Add(MetadataReference.CreateFromFile(Assembly.Load("System.Runtime.Extensions").Location)); MetadataReferences.Add(MetadataReference.CreateFromFile(Assembly.Load("Microsoft.Win32.Registry").Location)); MetadataReferences.Add(MetadataReference.CreateFromFile(typeof(UnrealBuildTool).Assembly.Location)); MetadataReferences.Add(MetadataReference.CreateFromFile(typeof(FileReference).Assembly.Location)); CSharpCompilationOptions CompilationOptions = new CSharpCompilationOptions( outputKind: OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release, warningLevel: 4, assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default, reportSuppressedDiagnostics: true ); CSharpCompilation Compilation = CSharpCompilation.Create( assemblyName: OutputAssemblyPath.GetFileNameWithoutAnyExtensions(), syntaxTrees: SyntaxTrees, references: MetadataReferences, options: CompilationOptions ); using (FileStream AssemblyStream = FileReference.Open(OutputAssemblyPath, FileMode.Create)) { EmitOptions EmitOptions = new EmitOptions( includePrivateMembers: true ); EmitResult Result = Compilation.Emit( peStream: AssemblyStream, options: EmitOptions); if (!Result.Success) { LogDiagnostics(Result.Diagnostics); return(null); } } return(Assembly.LoadFile(OutputAssemblyPath.FullName)); }
protected IOSProvisioningData(IOSProjectSettings ProjectSettings, bool bIsTVOS, bool bForDistribtion) { SigningCertificate = ProjectSettings.SigningCertificate; string MobileProvision = ProjectSettings.MobileProvision; FileReference ProjectFile = ProjectSettings.ProjectFile; if (!string.IsNullOrEmpty(SigningCertificate)) { // verify the certificate Process IPPProcess = new Process(); if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac) { string IPPCmd = "\"" + UnrealBuildTool.EngineDirectory + "/Binaries/DotNET/IOS/IPhonePackager.exe\" certificates " + ((ProjectFile != null) ? ("\"" + ProjectFile.ToString() + "\"") : "Engine") + " -bundlename " + ProjectSettings.BundleIdentifier + (bForDistribtion ? " -distribution" : ""); IPPProcess.StartInfo.WorkingDirectory = UnrealBuildTool.EngineDirectory.ToString(); IPPProcess.StartInfo.FileName = UnrealBuildTool.EngineDirectory + "/Build/BatchFiles/Mac/RunMono.sh"; IPPProcess.StartInfo.Arguments = IPPCmd; IPPProcess.OutputDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); IPPProcess.ErrorDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); } else { string IPPCmd = "certificates " + ((ProjectFile != null) ? ("\"" + ProjectFile.ToString() + "\"") : "Engine") + " -bundlename " + ProjectSettings.BundleIdentifier + (bForDistribtion ? " -distribution" : ""); IPPProcess.StartInfo.WorkingDirectory = UnrealBuildTool.EngineDirectory.ToString(); IPPProcess.StartInfo.FileName = UnrealBuildTool.EngineDirectory + "\\Binaries\\DotNET\\IOS\\IPhonePackager.exe"; IPPProcess.StartInfo.Arguments = IPPCmd; IPPProcess.OutputDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); IPPProcess.ErrorDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); } Utils.RunLocalProcess(IPPProcess); } else { SigningCertificate = bForDistribtion ? "iPhone Distribution" : "iPhone Developer"; bHaveCertificate = true; } if (!string.IsNullOrEmpty(MobileProvision)) { DirectoryReference MobileProvisionDir; if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac) { MobileProvisionDir = DirectoryReference.Combine(new DirectoryReference(Environment.GetEnvironmentVariable("HOME")), "Library", "MobileDevice", "Provisioning Profiles"); } else { MobileProvisionDir = DirectoryReference.Combine(DirectoryReference.GetSpecialFolder(Environment.SpecialFolder.LocalApplicationData), "Apple Computer", "MobileDevice", "Provisioning Profiles"); } FileReference PossibleMobileProvisionFile = FileReference.Combine(MobileProvisionDir, MobileProvision); if (FileReference.Exists(PossibleMobileProvisionFile)) { MobileProvisionFile = PossibleMobileProvisionFile; } } if (MobileProvisionFile == null || !bHaveCertificate) { SigningCertificate = ""; MobileProvision = ""; MobileProvisionFile = null; Log.TraceLog("Provision not specified or not found for " + ((ProjectFile != null) ? ProjectFile.GetFileNameWithoutAnyExtensions() : "UE4Game") + ", searching for compatible match..."); Process IPPProcess = new Process(); if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac) { string IPPCmd = "\"" + UnrealBuildTool.EngineDirectory + "/Binaries/DotNET/IOS/IPhonePackager.exe\" signing_match " + ((ProjectFile != null) ? ("\"" + ProjectFile.ToString() + "\"") : "Engine") + " -bundlename " + ProjectSettings.BundleIdentifier + (bIsTVOS ? " -tvos" : "") + (bForDistribtion ? " -distribution" : ""); IPPProcess.StartInfo.WorkingDirectory = UnrealBuildTool.EngineDirectory.ToString(); IPPProcess.StartInfo.FileName = UnrealBuildTool.EngineDirectory + "/Build/BatchFiles/Mac/RunMono.sh"; IPPProcess.StartInfo.Arguments = IPPCmd; IPPProcess.OutputDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); IPPProcess.ErrorDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); } else { string IPPCmd = "signing_match " + ((ProjectFile != null) ? ("\"" + ProjectFile.ToString() + "\"") : "Engine") + " -bundlename " + ProjectSettings.BundleIdentifier + (bIsTVOS ? " -tvos" : "") + (bForDistribtion ? " -distribution" : ""); IPPProcess.StartInfo.WorkingDirectory = UnrealBuildTool.EngineDirectory.ToString(); IPPProcess.StartInfo.FileName = UnrealBuildTool.EngineDirectory + "\\Binaries\\DotNET\\IOS\\IPhonePackager.exe"; IPPProcess.StartInfo.Arguments = IPPCmd; IPPProcess.OutputDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); IPPProcess.ErrorDataReceived += new DataReceivedEventHandler(IPPDataReceivedHandler); } Utils.RunLocalProcess(IPPProcess); if (MobileProvisionFile != null) { Log.TraceLog("Provision found for " + ((ProjectFile != null) ? ProjectFile.GetFileNameWithoutAnyExtensions() : "UE4Game") + ", Provision: " + MobileProvisionFile + " Certificate: " + SigningCertificate); } } // add to the dictionary SigningCertificate = SigningCertificate.Replace("\"", ""); // read the provision to get the UUID if (MobileProvisionFile == null) { Log.TraceLog("No matching provision file was discovered for {0}. Please ensure you have a compatible provision installed.", ProjectFile); } else if (!FileReference.Exists(MobileProvisionFile)) { Log.TraceLog("Selected mobile provision for {0} ({1}) was not found. Please ensure you have a compatible provision installed.", ProjectFile, MobileProvisionFile); } else { byte[] AllBytes = FileReference.ReadAllBytes(MobileProvisionFile); uint StartIndex = (uint)AllBytes.Length; uint EndIndex = (uint)AllBytes.Length; for (uint i = 0; i + 4 < AllBytes.Length; i++) { if (AllBytes[i] == '<' && AllBytes[i + 1] == '?' && AllBytes[i + 2] == 'x' && AllBytes[i + 3] == 'm' && AllBytes[i + 4] == 'l') { StartIndex = i; break; } } if (StartIndex < AllBytes.Length) { for (uint i = StartIndex; i + 7 < AllBytes.Length; i++) { if (AllBytes[i] == '<' && AllBytes[i + 1] == '/' && AllBytes[i + 2] == 'p' && AllBytes[i + 3] == 'l' && AllBytes[i + 4] == 'i' && AllBytes[i + 5] == 's' && AllBytes[i + 6] == 't' && AllBytes[i + 7] == '>') { EndIndex = i + 7; break; } } } if (StartIndex < AllBytes.Length && EndIndex < AllBytes.Length) { byte[] TextBytes = new byte[EndIndex - StartIndex]; Buffer.BlockCopy(AllBytes, (int)StartIndex, TextBytes, 0, (int)(EndIndex - StartIndex)); string AllText = Encoding.UTF8.GetString(TextBytes); int idx = AllText.IndexOf("<key>UUID</key>"); if (idx > 0) { idx = AllText.IndexOf("<string>", idx); if (idx > 0) { idx += "<string>".Length; MobileProvisionUUID = AllText.Substring(idx, AllText.IndexOf("</string>", idx) - idx); } } idx = AllText.IndexOf("<key>com.apple.developer.team-identifier</key>"); if (idx > 0) { idx = AllText.IndexOf("<string>", idx); if (idx > 0) { idx += "<string>".Length; TeamUUID = AllText.Substring(idx, AllText.IndexOf("</string>", idx) - idx); } } idx = AllText.IndexOf("<key>Name</key>"); if (idx > 0) { idx = AllText.IndexOf("<string>", idx); if (idx > 0) { idx += "<string>".Length; MobileProvisionName = AllText.Substring(idx, AllText.IndexOf("</string>", idx) - idx); } } } if (string.IsNullOrEmpty(MobileProvisionUUID) || string.IsNullOrEmpty(TeamUUID)) { MobileProvision = null; SigningCertificate = null; Log.TraceLog("Failed to parse the mobile provisioning profile."); } } }
protected override bool WriteMasterProjectFile(ProjectFile UBTProject) { bool bSuccess = true; string SolutionFileName = MasterProjectName + ".sln"; // Setup solution file content StringBuilder VCSolutionFileContent = new StringBuilder(); // Solution file header if (ProjectFileFormat == VCProjectFileFormat.VisualStudio2017) { VCSolutionFileContent.AppendLine("Microsoft Visual Studio Solution File, Format Version 12.00"); VCSolutionFileContent.AppendLine("# Visual Studio 15"); VCSolutionFileContent.AppendLine("VisualStudioVersion = 15.0.25807.0"); VCSolutionFileContent.AppendLine("MinimumVisualStudioVersion = 10.0.40219.1"); } else if (ProjectFileFormat == VCProjectFileFormat.VisualStudio2015) { VCSolutionFileContent.AppendLine("Microsoft Visual Studio Solution File, Format Version 12.00"); VCSolutionFileContent.AppendLine("# Visual Studio 14"); VCSolutionFileContent.AppendLine("VisualStudioVersion = 14.0.22310.1"); VCSolutionFileContent.AppendLine("MinimumVisualStudioVersion = 10.0.40219.1"); } else if (ProjectFileFormat == VCProjectFileFormat.VisualStudio2013) { VCSolutionFileContent.AppendLine("Microsoft Visual Studio Solution File, Format Version 12.00"); VCSolutionFileContent.AppendLine("# Visual Studio 2013"); } else if (ProjectFileFormat == VCProjectFileFormat.VisualStudio2012) { VCSolutionFileContent.AppendLine("Microsoft Visual Studio Solution File, Format Version 12.00"); VCSolutionFileContent.AppendLine("# Visual Studio 2012"); } else { throw new BuildException("Unexpected ProjectFileFormat"); } // Find the projects for ShaderCompileWorker and UnrealLightmass ProjectFile ShaderCompileWorkerProject = null; ProjectFile UnrealLightmassProject = null; foreach (ProjectFile Project in AllProjectFiles) { if (Project.ProjectTargets.Count == 1) { FileReference TargetFilePath = Project.ProjectTargets[0].TargetFilePath; if (TargetFilePath != null) { string TargetFileName = TargetFilePath.GetFileNameWithoutAnyExtensions(); if (TargetFileName.Equals("ShaderCompileWorker", StringComparison.InvariantCultureIgnoreCase)) { ShaderCompileWorkerProject = Project; } else if (TargetFileName.Equals("UnrealLightmass", StringComparison.InvariantCultureIgnoreCase)) { UnrealLightmassProject = Project; } } if (ShaderCompileWorkerProject != null && UnrealLightmassProject != null) { break; } } } // Solution folders, files and project entries { // This the GUID that Visual Studio uses to identify a solution folder string SolutionFolderEntryGUID = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}"; // Solution folders { List <MasterProjectFolder> AllSolutionFolders = new List <MasterProjectFolder>(); System.Action <List <MasterProjectFolder> /* Folders */> GatherFoldersFunction = null; GatherFoldersFunction = FolderList => { AllSolutionFolders.AddRange(FolderList); foreach (MasterProjectFolder CurSubFolder in FolderList) { GatherFoldersFunction(CurSubFolder.SubFolders); } }; GatherFoldersFunction(RootFolder.SubFolders); foreach (VisualStudioSolutionFolder CurFolder in AllSolutionFolders) { string FolderGUIDString = CurFolder.FolderGUID.ToString("B").ToUpperInvariant(); VCSolutionFileContent.AppendLine("Project(\"" + SolutionFolderEntryGUID + "\") = \"" + CurFolder.FolderName + "\", \"" + CurFolder.FolderName + "\", \"" + FolderGUIDString + "\""); // Add any files that are inlined right inside the solution folder if (CurFolder.Files.Count > 0) { VCSolutionFileContent.AppendLine(" ProjectSection(SolutionItems) = preProject"); foreach (string CurFile in CurFolder.Files) { // Syntax is: <relative file path> = <relative file path> VCSolutionFileContent.AppendLine(" "+ CurFile + " = " + CurFile); } VCSolutionFileContent.AppendLine(" EndProjectSection"); } VCSolutionFileContent.AppendLine("EndProject"); } } // Project files foreach (MSBuildProjectFile CurProject in AllProjectFiles) { // Visual Studio uses different GUID types depending on the project type string ProjectTypeGUID = CurProject.ProjectTypeGUID; // NOTE: The project name in the solution doesn't actually *have* to match the project file name on disk. However, // we prefer it when it does match so we use the actual file name here. string ProjectNameInSolution = CurProject.ProjectFilePath.GetFileNameWithoutExtension(); // Use the existing project's GUID that's already known to us string ProjectGUID = CurProject.ProjectGUID.ToString("B").ToUpperInvariant(); VCSolutionFileContent.AppendLine("Project(\"" + ProjectTypeGUID + "\") = \"" + ProjectNameInSolution + "\", \"" + CurProject.ProjectFilePath.MakeRelativeTo(ProjectFileGenerator.MasterProjectPath) + "\", \"" + ProjectGUID + "\""); // Setup dependency on UnrealBuildTool, if we need that. This makes sure that UnrealBuildTool is // freshly compiled before kicking off any build operations on this target project if (!CurProject.IsStubProject) { List <ProjectFile> Dependencies = new List <ProjectFile>(); if (CurProject.IsGeneratedProject && UBTProject != null && CurProject != UBTProject) { Dependencies.Add(UBTProject); Dependencies.AddRange(UBTProject.DependsOnProjects); } if (bEditorDependsOnShaderCompileWorker && !bUsePrecompiled && CurProject.IsGeneratedProject && ShaderCompileWorkerProject != null && CurProject.ProjectTargets.Any(x => x.TargetRules != null && x.TargetRules.Type == TargetType.Editor)) { Dependencies.Add(ShaderCompileWorkerProject); } Dependencies.AddRange(CurProject.DependsOnProjects); if (Dependencies.Count > 0) { VCSolutionFileContent.AppendLine("\tProjectSection(ProjectDependencies) = postProject"); // Setup any addition dependencies this project has... foreach (ProjectFile DependsOnProject in Dependencies) { string DependsOnProjectGUID = ((MSBuildProjectFile)DependsOnProject).ProjectGUID.ToString("B").ToUpperInvariant(); VCSolutionFileContent.AppendLine("\t\t" + DependsOnProjectGUID + " = " + DependsOnProjectGUID); } VCSolutionFileContent.AppendLine("\tEndProjectSection"); } } VCSolutionFileContent.AppendLine("EndProject"); } // Add the visualizers at the solution level. Doesn't seem to be picked up from a makefile project in VS2017 15.8.5. VCSolutionFileContent.AppendLine(String.Format("Project(\"{{2150E333-8FDC-42A3-9474-1A3956D46DE8}}\") = \"Visualizers\", \"Visualizers\", \"{0}\"", Guid.NewGuid().ToString())); VCSolutionFileContent.AppendLine("\tProjectSection(SolutionItems) = preProject"); VCSolutionFileContent.AppendLine("\t\tEngine\\Extras\\VisualStudioDebugging\\UE4.natvis = Engine\\Extras\\VisualStudioDebugging\\UE4.natvis"); VCSolutionFileContent.AppendLine("\tEndProjectSection"); VCSolutionFileContent.AppendLine("EndProject"); } // Solution configuration platforms. This is just a list of all of the platforms and configurations that // appear in Visual Studio's build configuration selector. List <VCSolutionConfigCombination> SolutionConfigCombinations = new List <VCSolutionConfigCombination>(); // The "Global" section has source control, solution configurations, project configurations, // preferences, and project hierarchy data { VCSolutionFileContent.AppendLine("Global"); { { VCSolutionFileContent.AppendLine(" GlobalSection(SolutionConfigurationPlatforms) = preSolution"); Dictionary <string, Tuple <UnrealTargetConfiguration, TargetType> > SolutionConfigurationsValidForProjects = new Dictionary <string, Tuple <UnrealTargetConfiguration, TargetType> >(); HashSet <UnrealTargetPlatform> PlatformsValidForProjects = new HashSet <UnrealTargetPlatform>(); foreach (UnrealTargetConfiguration CurConfiguration in SupportedConfigurations) { if (InstalledPlatformInfo.IsValidConfiguration(CurConfiguration, EProjectType.Code)) { foreach (UnrealTargetPlatform CurPlatform in SupportedPlatforms) { if (InstalledPlatformInfo.IsValidPlatform(CurPlatform, EProjectType.Code)) { foreach (ProjectFile CurProject in AllProjectFiles) { if (!CurProject.IsStubProject) { if (CurProject.ProjectTargets.Count == 0) { throw new BuildException("Expecting project '" + CurProject.ProjectFilePath + "' to have at least one ProjectTarget associated with it!"); } // Figure out the set of valid target configuration names foreach (ProjectTarget ProjectTarget in CurProject.ProjectTargets) { if (VCProjectFile.IsValidProjectPlatformAndConfiguration(ProjectTarget, CurPlatform, CurConfiguration)) { PlatformsValidForProjects.Add(CurPlatform); // Default to a target configuration name of "Game", since that will collapse down to an empty string TargetType TargetType = TargetType.Game; if (ProjectTarget.TargetRules != null) { TargetType = ProjectTarget.TargetRules.Type; } string SolutionConfigName = MakeSolutionConfigurationName(CurConfiguration, TargetType); SolutionConfigurationsValidForProjects[SolutionConfigName] = new Tuple <UnrealTargetConfiguration, TargetType>(CurConfiguration, TargetType); } } } } } } } } foreach (UnrealTargetPlatform CurPlatform in PlatformsValidForProjects) { foreach (KeyValuePair <string, Tuple <UnrealTargetConfiguration, TargetType> > SolutionConfigKeyValue in SolutionConfigurationsValidForProjects) { // e.g. "Development|Win64 = Development|Win64" string SolutionConfigName = SolutionConfigKeyValue.Key; UnrealTargetConfiguration Configuration = SolutionConfigKeyValue.Value.Item1; TargetType TargetType = SolutionConfigKeyValue.Value.Item2; string SolutionPlatformName = CurPlatform.ToString(); string SolutionConfigAndPlatformPair = SolutionConfigName + "|" + SolutionPlatformName; SolutionConfigCombinations.Add( new VCSolutionConfigCombination { VCSolutionConfigAndPlatformName = SolutionConfigAndPlatformPair, Configuration = Configuration, Platform = CurPlatform, TargetConfigurationName = TargetType } ); } } // Sort the list of solution platform strings alphabetically (Visual Studio prefers it) SolutionConfigCombinations.Sort( new Comparison <VCSolutionConfigCombination>( (x, y) => { return(String.Compare(x.VCSolutionConfigAndPlatformName, y.VCSolutionConfigAndPlatformName, StringComparison.InvariantCultureIgnoreCase)); } ) ); HashSet <string> AppendedSolutionConfigAndPlatformNames = new HashSet <string>(StringComparer.InvariantCultureIgnoreCase); foreach (VCSolutionConfigCombination SolutionConfigCombination in SolutionConfigCombinations) { // We alias "Game" and "Program" to both have the same solution configuration, so we're careful not to add the same combination twice. if (!AppendedSolutionConfigAndPlatformNames.Contains(SolutionConfigCombination.VCSolutionConfigAndPlatformName)) { VCSolutionFileContent.AppendLine(" "+ SolutionConfigCombination.VCSolutionConfigAndPlatformName + " = " + SolutionConfigCombination.VCSolutionConfigAndPlatformName); AppendedSolutionConfigAndPlatformNames.Add(SolutionConfigCombination.VCSolutionConfigAndPlatformName); } } VCSolutionFileContent.AppendLine(" EndGlobalSection"); } // Assign each project's "project configuration" to our "solution platform + configuration" pairs. This // also sets up which projects are actually built when building the solution. { VCSolutionFileContent.AppendLine(" GlobalSection(ProjectConfigurationPlatforms) = postSolution"); foreach (MSBuildProjectFile CurProject in AllProjectFiles) { // NOTE: We don't emit solution configuration entries for "stub" projects. Those projects are only // built using UnrealBuildTool and don't require a presence in the solution project list // NOTE: We also process projects that were "imported" here, hoping to match those to our solution // configurations. In some cases this may not be successful though. Imported projects // should always be carefully setup to match our project generator's solution configs. if (!CurProject.IsStubProject) { if (CurProject.ProjectTargets.Count == 0) { throw new BuildException("Expecting project '" + CurProject.ProjectFilePath + "' to have at least one ProjectTarget associated with it!"); } foreach (VCSolutionConfigCombination SolutionConfigCombination in SolutionConfigCombinations) { // Get the context for the current solution context MSBuildProjectContext ProjectContext = CurProject.GetMatchingProjectContext(SolutionConfigCombination.TargetConfigurationName, SolutionConfigCombination.Configuration, SolutionConfigCombination.Platform); // Write the solution mapping (e.g. "{4232C52C-680F-4850-8855-DC39419B5E9B}.Debug|iOS.ActiveCfg = iOS_Debug|Win32") string CurProjectGUID = CurProject.ProjectGUID.ToString("B").ToUpperInvariant(); VCSolutionFileContent.AppendLine(" {0}.{1}.ActiveCfg = {2}", CurProjectGUID, SolutionConfigCombination.VCSolutionConfigAndPlatformName, ProjectContext.Name); if (ProjectContext.bBuildByDefault) { VCSolutionFileContent.AppendLine(" {0}.{1}.Build.0 = {2}", CurProjectGUID, SolutionConfigCombination.VCSolutionConfigAndPlatformName, ProjectContext.Name); if (ProjectContext.bDeployByDefault) { VCSolutionFileContent.AppendLine(" {0}.{1}.Deploy.0 = {2}", CurProjectGUID, SolutionConfigCombination.VCSolutionConfigAndPlatformName, ProjectContext.Name); } } } } } VCSolutionFileContent.AppendLine(" EndGlobalSection"); } // Setup other solution properties { // HideSolutionNode sets whether or not the top-level solution entry is completely hidden in the UI. // We don't want that, as we need users to be able to right click on the solution tree item. VCSolutionFileContent.AppendLine(" GlobalSection(SolutionProperties) = preSolution"); VCSolutionFileContent.AppendLine(" HideSolutionNode = FALSE"); VCSolutionFileContent.AppendLine(" EndGlobalSection"); } // Solution directory hierarchy { VCSolutionFileContent.AppendLine(" GlobalSection(NestedProjects) = preSolution"); // Every entry in this section is in the format "Guid1 = Guid2". Guid1 is the child project (or solution // filter)'s GUID, and Guid2 is the solution filter directory to parent the child project (or solution // filter) to. This sets up the hierarchical solution explorer tree for all solution folders and projects. System.Action <StringBuilder /* VCSolutionFileContent */, List <MasterProjectFolder> /* Folders */> FolderProcessorFunction = null; FolderProcessorFunction = (LocalVCSolutionFileContent, LocalMasterProjectFolders) => { foreach (VisualStudioSolutionFolder CurFolder in LocalMasterProjectFolders) { string CurFolderGUIDString = CurFolder.FolderGUID.ToString("B").ToUpperInvariant(); foreach (MSBuildProjectFile ChildProject in CurFolder.ChildProjects) { // e.g. "{BF6FB09F-A2A6-468F-BE6F-DEBE07EAD3EA} = {C43B6BB5-3EF0-4784-B896-4099753BCDA9}" LocalVCSolutionFileContent.AppendLine(" "+ ChildProject.ProjectGUID.ToString("B").ToUpperInvariant() + " = " + CurFolderGUIDString); } foreach (VisualStudioSolutionFolder SubFolder in CurFolder.SubFolders) { // e.g. "{BF6FB09F-A2A6-468F-BE6F-DEBE07EAD3EA} = {C43B6BB5-3EF0-4784-B896-4099753BCDA9}" LocalVCSolutionFileContent.AppendLine(" "+ SubFolder.FolderGUID.ToString("B").ToUpperInvariant() + " = " + CurFolderGUIDString); } // Recurse into subfolders FolderProcessorFunction(LocalVCSolutionFileContent, CurFolder.SubFolders); } }; FolderProcessorFunction(VCSolutionFileContent, RootFolder.SubFolders); VCSolutionFileContent.AppendLine(" EndGlobalSection"); } } VCSolutionFileContent.AppendLine("EndGlobal"); } // Save the solution file if (bSuccess) { string SolutionFilePath = FileReference.Combine(MasterProjectPath, SolutionFileName).FullName; bSuccess = WriteFileIfChanged(SolutionFilePath, VCSolutionFileContent.ToString()); } // Save a solution config file which selects the development editor configuration by default. if (bSuccess && bWriteSolutionOptionFile) { // Figure out the filename for the SUO file. VS will automatically import the options from earlier versions if necessary. FileReference SolutionOptionsFileName; switch (ProjectFileFormat) { case VCProjectFileFormat.VisualStudio2012: SolutionOptionsFileName = FileReference.Combine(MasterProjectPath, Path.ChangeExtension(SolutionFileName, "v11.suo")); break; case VCProjectFileFormat.VisualStudio2013: SolutionOptionsFileName = FileReference.Combine(MasterProjectPath, Path.ChangeExtension(SolutionFileName, "v12.suo")); break; case VCProjectFileFormat.VisualStudio2015: SolutionOptionsFileName = FileReference.Combine(MasterProjectPath, ".vs", Path.GetFileNameWithoutExtension(SolutionFileName), "v14", ".suo"); break; case VCProjectFileFormat.VisualStudio2017: SolutionOptionsFileName = FileReference.Combine(MasterProjectPath, ".vs", Path.GetFileNameWithoutExtension(SolutionFileName), "v15", ".suo"); break; default: throw new BuildException("Unsupported Visual Studio version"); } // Check it doesn't exist before overwriting it. Since these files store the user's preferences, it'd be bad form to overwrite them. if (!FileReference.Exists(SolutionOptionsFileName)) { DirectoryReference.CreateDirectory(SolutionOptionsFileName.Directory); VCSolutionOptions Options = new VCSolutionOptions(ProjectFileFormat); // Set the default configuration and startup project VCSolutionConfigCombination DefaultConfig = SolutionConfigCombinations.Find(x => x.Configuration == UnrealTargetConfiguration.Development && x.Platform == UnrealTargetPlatform.Win64 && x.TargetConfigurationName == TargetType.Editor); if (DefaultConfig != null) { List <VCBinarySetting> Settings = new List <VCBinarySetting>(); Settings.Add(new VCBinarySetting("ActiveCfg", DefaultConfig.VCSolutionConfigAndPlatformName)); if (DefaultProject != null) { Settings.Add(new VCBinarySetting("StartupProject", ((MSBuildProjectFile)DefaultProject).ProjectGUID.ToString("B"))); } Options.SetConfiguration(Settings); } // Mark all the projects as closed by default, apart from the startup project VCSolutionExplorerState ExplorerState = new VCSolutionExplorerState(); if (ProjectFileFormat >= VCProjectFileFormat.VisualStudio2017) { BuildSolutionExplorerState_VS2017(RootFolder, "", ExplorerState, DefaultProject); } else { BuildSolutionExplorerState_VS2015(AllProjectFiles, ExplorerState, DefaultProject, IncludeEnginePrograms); } Options.SetExplorerState(ExplorerState); // Write the file if (Options.Sections.Count > 0) { Options.Write(SolutionOptionsFileName.FullName); } } } return(bSuccess); }
/// <summary> /// Attempt to merge a child plugin up into a parent plugin (via file naming scheme). Very little merging happens /// but it does allow for platform extensions to extend a plugin with module files /// </summary> /// <param name="Child">Child plugin that needs to merge to a main, parent plugin</param> /// <param name="Filename">Child plugin's filename, used to determine the parent's name</param> private static void TryMergeWithParent(PluginInfo Child, FileReference Filename) { // find the parent PluginInfo Parent = null; string[] Tokens = Filename.GetFileNameWithoutAnyExtensions().Split("_".ToCharArray()); if (Tokens.Length == 2) { string ParentPluginName = Tokens[0]; foreach (KeyValuePair <DirectoryReference, List <PluginInfo> > Pair in PluginInfoCache) { Parent = Pair.Value.FirstOrDefault(x => x.Name.Equals(ParentPluginName, StringComparison.InvariantCultureIgnoreCase)); if (Parent != null) { break; } } } // did we find a parent plugin? if (Parent == null) { throw new BuildException("Child plugin {0} was not named properly. It should be in the form <ParentPlugin>_<Platform>.uplugin", Filename); } // validate child plugin file name string PlatformName = Tokens[1]; if (!IsValidChildPluginSuffix(PlatformName)) { Log.TraceWarning("Ignoring child plugin: {0} - Unknown suffix \"{1}\". Expected valid platform or group", Child.File.GetFileName(), PlatformName); return; } // add our uplugin file to the existing plugin to be used to search for modules later Parent.ChildFiles.Add(Child.File); // merge the supported platforms Parent.Descriptor.MergeSupportedTargetPlatforms(Child.Descriptor.SupportedTargetPlatforms); // make sure we are whitelisted for any modules we list, if the parent had a whitelist if (Child.Descriptor.Modules != null) { // this should cause an error if it's invalid platform name UnrealTargetPlatform Platform = UnrealTargetPlatform.Parse(PlatformName); foreach (ModuleDescriptor ChildModule in Child.Descriptor.Modules) { ModuleDescriptor ParentModule = Parent.Descriptor.Modules.FirstOrDefault(x => x.Name.Equals(ChildModule.Name) && x.Type == ChildModule.Type); if (ParentModule != null) { // merge white/blacklists (if the parent had a list, and child didn't specify a list, just add the child platform to the parent list - for white and black!) if (ParentModule.WhitelistPlatforms != null && ParentModule.WhitelistPlatforms.Length > 0) { List <UnrealTargetPlatform> Whitelist = ParentModule.WhitelistPlatforms.ToList(); if (ChildModule.WhitelistPlatforms != null && ChildModule.WhitelistPlatforms.Length > 0) { Whitelist.AddRange(ChildModule.WhitelistPlatforms); } else { Whitelist.Add(Platform); } ParentModule.WhitelistPlatforms = Whitelist.ToArray(); } if (ParentModule.BlacklistPlatforms != null && ParentModule.BlacklistPlatforms.Length > 0) { if (ChildModule.BlacklistPlatforms != null && ChildModule.BlacklistPlatforms.Length > 0) { List <UnrealTargetPlatform> Blacklist = ParentModule.BlacklistPlatforms.ToList(); Blacklist.AddRange(ChildModule.BlacklistPlatforms); ParentModule.BlacklistPlatforms = Blacklist.ToArray(); } } } } } // @todo platplug: what else do we want to support merging?!? }
private FileReference GetExecutableFilename(ProjectFile Project, ProjectTarget Target, UnrealTargetPlatform Platform, UnrealTargetConfiguration Configuration) { TargetRules TargetRulesObject = Target.TargetRules; FileReference TargetFilePath = Target.TargetFilePath; string TargetName = TargetFilePath == null?Project.ProjectFilePath.GetFileNameWithoutExtension() : TargetFilePath.GetFileNameWithoutAnyExtensions(); string UBTPlatformName = Platform.ToString(); string UBTConfigurationName = Configuration.ToString(); string ProjectName = Project.ProjectFilePath.GetFileNameWithoutExtension(); // Setup output path UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatform(Platform); // Figure out if this is a monolithic build bool bShouldCompileMonolithic = BuildPlatform.ShouldCompileMonolithicBinary(Platform); if (TargetRulesObject != null) { bShouldCompileMonolithic |= (Target.CreateRulesDelegate(Platform, Configuration).GetLegacyLinkType(Platform, Configuration) == TargetLinkType.Monolithic); } TargetType TargetRulesType = Target.TargetRules == null ? TargetType.Program : Target.TargetRules.Type; // Get the output directory DirectoryReference RootDirectory = UnrealBuildTool.EngineDirectory; if (TargetRulesType != TargetType.Program && (bShouldCompileMonolithic || TargetRulesObject.BuildEnvironment == TargetBuildEnvironment.Unique) && !TargetRulesObject.bOutputToEngineBinaries) { if (OnlyGameProject != null && TargetFilePath.IsUnderDirectory(OnlyGameProject.Directory)) { RootDirectory = OnlyGameProject.Directory; } else { FileReference ProjectFileName; if (UProjectInfo.TryGetProjectFileName(ProjectName, out ProjectFileName)) { RootDirectory = ProjectFileName.Directory; } } } if (TargetRulesType == TargetType.Program && (TargetRulesObject == null || !TargetRulesObject.bOutputToEngineBinaries)) { FileReference ProjectFileName; if (UProjectInfo.TryGetProjectForTarget(TargetName, out ProjectFileName)) { RootDirectory = ProjectFileName.Directory; } } // Get the output directory DirectoryReference OutputDirectory = DirectoryReference.Combine(RootDirectory, "Binaries", UBTPlatformName); // Get the executable name (minus any platform or config suffixes) string BaseExeName = TargetName; if (!bShouldCompileMonolithic && TargetRulesType != TargetType.Program) { // Figure out what the compiled binary will be called so that we can point the IDE to the correct file string TargetConfigurationName = TargetRulesType.ToString(); if (TargetConfigurationName != TargetType.Game.ToString() && TargetConfigurationName != TargetType.Program.ToString()) { BaseExeName = "UE4" + TargetConfigurationName; } } // Make the output file path string ExecutableFilename = FileReference.Combine(OutputDirectory, BaseExeName).ToString(); if ((Configuration != UnrealTargetConfiguration.DebugGame || bShouldCompileMonolithic)) { ExecutableFilename += "-" + UBTPlatformName + "-" + UBTConfigurationName; } ExecutableFilename += TargetRulesObject.Architecture; ExecutableFilename += BuildPlatform.GetBinaryExtension(UEBuildBinaryType.Executable); return(FileReference.MakeFromNormalizedFullPath(ExecutableFilename)); }
/// <summary> /// Attempt to merge a child plugin up into a parent plugin (via file naming scheme). Very little merging happens /// but it does allow for platform extensions to extend a plugin with module files /// </summary> /// <param name="Child">Child plugin that needs to merge to a main, parent plugin</param> /// <param name="Filename">Child plugin's filename, used to determine the parent's name</param> private static void TryMergeWithParent(PluginInfo Child, FileReference Filename) { // find the parent PluginInfo Parent = null; string[] Tokens = Filename.GetFileNameWithoutAnyExtensions().Split("_".ToCharArray()); if (Tokens.Length == 2) { string ParentPluginName = Tokens[0]; foreach (KeyValuePair <DirectoryReference, List <PluginInfo> > Pair in PluginInfoCache) { Parent = Pair.Value.FirstOrDefault(x => x.Name.Equals(ParentPluginName, StringComparison.InvariantCultureIgnoreCase)); if (Parent != null) { break; } } } else { throw new BuildException("Platform extension plugin {0} was named improperly. It must be in the form <ParentPlugin>_<Platform>.uplugin", Filename); } // did we find a parent plugin? if (Parent == null) { throw new BuildException("Unable to find parent plugin {0} for platform extension plugin {1}. Make sure {0}.uplugin exists.", Tokens[0], Filename); } // validate child plugin file name string PlatformName = Tokens[1]; if (!IsValidChildPluginSuffix(PlatformName)) { Log.TraceWarning("Ignoring child plugin: {0} - Unknown suffix \"{1}\". Expected valid platform or group", Child.File.GetFileName(), PlatformName); return; } // add our uplugin file to the existing plugin to be used to search for modules later Parent.ChildFiles.Add(Child.File); // this should cause an error if it's invalid platform name //UnrealTargetPlatform Platform = UnrealTargetPlatform.Parse(PlatformName); // merge the supported platforms if (Child.Descriptor.SupportedTargetPlatforms != null) { if (Parent.Descriptor.SupportedTargetPlatforms == null) { Parent.Descriptor.SupportedTargetPlatforms = Child.Descriptor.SupportedTargetPlatforms; } else { Parent.Descriptor.SupportedTargetPlatforms = Parent.Descriptor.SupportedTargetPlatforms.Union(Child.Descriptor.SupportedTargetPlatforms).ToList(); } } // make sure we are whitelisted for any modules we list if (Child.Descriptor.Modules != null) { if (Parent.Descriptor.Modules == null) { Parent.Descriptor.Modules = Child.Descriptor.Modules; } else { foreach (ModuleDescriptor ChildModule in Child.Descriptor.Modules) { ModuleDescriptor ParentModule = Parent.Descriptor.Modules.FirstOrDefault(x => x.Name.Equals(ChildModule.Name) && x.Type == ChildModule.Type); if (ParentModule != null) { // merge white/blacklists (if the parent had a list, and child didn't specify a list, just add the child platform to the parent list - for white and black!) if (ChildModule.WhitelistPlatforms != null) { if (ParentModule.WhitelistPlatforms == null) { ParentModule.WhitelistPlatforms = ChildModule.WhitelistPlatforms; } else { ParentModule.WhitelistPlatforms = ParentModule.WhitelistPlatforms.Union(ChildModule.WhitelistPlatforms).ToList(); } } if (ChildModule.BlacklistPlatforms != null) { if (ParentModule.BlacklistPlatforms == null) { ParentModule.BlacklistPlatforms = ChildModule.BlacklistPlatforms; } else { ParentModule.BlacklistPlatforms = ParentModule.BlacklistPlatforms.Union(ChildModule.BlacklistPlatforms).ToList(); } } } else { Parent.Descriptor.Modules.Add(ChildModule); } } } } // make sure we are whitelisted for any plugins we list if (Child.Descriptor.Plugins != null) { if (Parent.Descriptor.Plugins == null) { Parent.Descriptor.Plugins = Child.Descriptor.Plugins; } else { foreach (PluginReferenceDescriptor ChildPluginReference in Child.Descriptor.Plugins) { PluginReferenceDescriptor ParentPluginReference = Parent.Descriptor.Plugins.FirstOrDefault(x => x.Name.Equals(ChildPluginReference.Name)); if (ParentPluginReference != null) { // we only need to whitelist the platform if the parent had a whitelist (otherwise, we could mistakenly remove all other platforms) if (ParentPluginReference.WhitelistPlatforms != null) { if (ChildPluginReference.WhitelistPlatforms != null) { ParentPluginReference.WhitelistPlatforms = ParentPluginReference.WhitelistPlatforms.Union(ChildPluginReference.WhitelistPlatforms).ToList(); } } // if we want to blacklist a platform, add it even if the parent didn't have a blacklist. this won't cause problems with other platforms if (ChildPluginReference.BlacklistPlatforms != null) { if (ParentPluginReference.BlacklistPlatforms == null) { ParentPluginReference.BlacklistPlatforms = ChildPluginReference.BlacklistPlatforms; } else { ParentPluginReference.BlacklistPlatforms = ParentPluginReference.BlacklistPlatforms.Union(ChildPluginReference.BlacklistPlatforms).ToList(); } } } else { Parent.Descriptor.Plugins.Add(ChildPluginReference); } } } } // @todo platplug: what else do we want to support merging?!? }