/// <inheritdoc /> public override void GenerateProject(Project project) { var csProjectFileContent = new StringBuilder(); var vsProject = (VisualStudioProject)project; var projectFileToolVersion = ProjectFileToolVersion; var projectDirectory = Path.GetDirectoryName(project.Path); var defaultTarget = project.Targets[0]; var defaultConfiguration = defaultTarget.Configurations.First(); var defaultArchitecture = defaultTarget.Architectures.First(); var projectTypes = ProjectTypeGuids.ToOption(ProjectTypeGuids.WindowsCSharp); if (vsProject.CSharp.UseFlaxVS && VisualStudioInstance.HasFlaxVS) { projectTypes = ProjectTypeGuids.ToOption(ProjectTypeGuids.FlaxVS) + ';' + projectTypes; } // Header csProjectFileContent.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); csProjectFileContent.AppendLine(string.Format("<Project DefaultTargets=\"Build\" ToolsVersion=\"{0}\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">", projectFileToolVersion)); csProjectFileContent.AppendLine(" <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />"); // Properties csProjectFileContent.AppendLine(" <PropertyGroup>"); csProjectFileContent.AppendLine(string.Format(" <Configuration Condition=\" '$(Configuration)' == '' \">{0}</Configuration>", defaultConfiguration)); csProjectFileContent.AppendLine(string.Format(" <Platform Condition=\" '$(Platform)' == '' \">{0}</Platform>", defaultArchitecture)); csProjectFileContent.AppendLine(string.Format(" <ProjectTypeGuids>{0}</ProjectTypeGuids>", projectTypes)); csProjectFileContent.AppendLine(string.Format(" <ProjectGuid>{0}</ProjectGuid>", vsProject.ProjectGuid.ToString("B").ToUpperInvariant())); switch (project.OutputType ?? defaultTarget.OutputType) { case TargetOutputType.Executable: csProjectFileContent.AppendLine(" <OutputType>Exe</OutputType>"); break; case TargetOutputType.Library: csProjectFileContent.AppendLine(" <OutputType>Library</OutputType>"); break; default: throw new ArgumentOutOfRangeException(); } csProjectFileContent.AppendLine(string.Format(" <RootNamespace>{0}</RootNamespace>", project.Name)); csProjectFileContent.AppendLine(string.Format(" <AssemblyName>{0}.CSharp</AssemblyName>", project.Name)); csProjectFileContent.AppendLine(string.Format(" <TargetFrameworkVersion>{0}</TargetFrameworkVersion>", "v4.5")); csProjectFileContent.AppendLine(" <LangVersion>7.3</LangVersion>"); csProjectFileContent.AppendLine(" <FileAlignment>512</FileAlignment>"); csProjectFileContent.AppendLine(" <TargetFrameworkProfile />"); csProjectFileContent.AppendLine(" </PropertyGroup>"); // Configurations foreach (var configuration in project.Configurations) { var defines = string.Join(";", project.Defines); if (configuration.TargetBuildOptions.ScriptingAPI.Defines.Count != 0) { if (defines.Length != 0) { defines += ";"; } defines += string.Join(";", configuration.TargetBuildOptions.ScriptingAPI.Defines); } var outputPath = Utilities.MakePathRelativeTo(project.CSharp.OutputPath ?? configuration.TargetBuildOptions.OutputFolder, projectDirectory); var intermediateOutputPath = Utilities.MakePathRelativeTo(project.CSharp.IntermediateOutputPath ?? Path.Combine(configuration.TargetBuildOptions.IntermediateFolder, "CSharp"), projectDirectory); csProjectFileContent.AppendLine(string.Format(" <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == '{0}' \">", configuration.Name)); csProjectFileContent.AppendLine(" <DebugSymbols>true</DebugSymbols>"); csProjectFileContent.AppendLine(" <DebugType>portable</DebugType>"); csProjectFileContent.AppendLine(string.Format(" <Optimize>{0}</Optimize>", configuration.Configuration == TargetConfiguration.Debug ? "false" : "true")); csProjectFileContent.AppendLine(string.Format(" <OutputPath>{0}\\</OutputPath>", outputPath)); csProjectFileContent.AppendLine(string.Format(" <BaseIntermediateOutputPath>{0}\\</BaseIntermediateOutputPath>", intermediateOutputPath)); csProjectFileContent.AppendLine(string.Format(" <IntermediateOutputPath>{0}\\</IntermediateOutputPath>", intermediateOutputPath)); csProjectFileContent.AppendLine(string.Format(" <DefineConstants>{0}</DefineConstants>", defines)); csProjectFileContent.AppendLine(" <ErrorReport>prompt</ErrorReport>"); csProjectFileContent.AppendLine(" <WarningLevel>4</WarningLevel>"); csProjectFileContent.AppendLine(" <AllowUnsafeBlocks>true</AllowUnsafeBlocks>"); if (configuration.TargetBuildOptions.ScriptingAPI.IgnoreMissingDocumentationWarnings) { csProjectFileContent.AppendLine(" <NoWarn>1591</NoWarn>"); } csProjectFileContent.AppendLine(string.Format(" <DocumentationFile>{0}\\{1}.CSharp.xml</DocumentationFile>", outputPath, project.Name)); csProjectFileContent.AppendLine(" <UseVSHostingProcess>true</UseVSHostingProcess>"); csProjectFileContent.AppendLine(" </PropertyGroup>"); } // References csProjectFileContent.AppendLine(" <ItemGroup>"); foreach (var reference in project.CSharp.SystemReferences) { csProjectFileContent.AppendLine(string.Format(" <Reference Include=\"{0}\" />", reference)); } foreach (var reference in project.CSharp.FileReferences) { csProjectFileContent.AppendLine(string.Format(" <Reference Include=\"{0}\">", Path.GetFileNameWithoutExtension(reference))); csProjectFileContent.AppendLine(string.Format(" <HintPath>{0}</HintPath>", Utilities.MakePathRelativeTo(reference, projectDirectory))); csProjectFileContent.AppendLine(" </Reference>"); } foreach (var dependency in project.Dependencies) { csProjectFileContent.AppendLine(string.Format(" <ProjectReference Include=\"{0}\">", Utilities.MakePathRelativeTo(dependency.Path, projectDirectory))); csProjectFileContent.AppendLine(string.Format(" <Project>{0}</Project>", ((VisualStudioProject)dependency).ProjectGuid.ToString("B").ToUpperInvariant())); csProjectFileContent.AppendLine(string.Format(" <Name>{0}</Name>", dependency.Name)); csProjectFileContent.AppendLine(" </ProjectReference>"); } csProjectFileContent.AppendLine(" </ItemGroup>"); // Files and folders csProjectFileContent.AppendLine(" <ItemGroup>"); var files = new List <string>(); if (project.SourceFiles != null) { files.AddRange(project.SourceFiles); } if (project.SourceDirectories != null) { foreach (var folder in project.SourceDirectories) { files.AddRange(Directory.GetFiles(folder, "*", SearchOption.AllDirectories)); } } foreach (var file in files) { string fileType; if (file.EndsWith(".cs", StringComparison.OrdinalIgnoreCase)) { fileType = "Compile"; } else { fileType = "None"; } var projectPath = Utilities.MakePathRelativeTo(file, projectDirectory); csProjectFileContent.AppendLine(string.Format(" <{0} Include=\"{1}\" />", fileType, projectPath)); } if (project.GeneratedSourceFiles != null) { foreach (var file in project.GeneratedSourceFiles) { string fileType; if (file.EndsWith(".cs", StringComparison.OrdinalIgnoreCase)) { fileType = "Compile"; } else { fileType = "None"; } csProjectFileContent.AppendLine(string.Format(" <{0} Visible=\"false\" Include =\"{1}\" />", fileType, file)); } } csProjectFileContent.AppendLine(" </ItemGroup>"); // End csProjectFileContent.AppendLine(" <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />"); csProjectFileContent.AppendLine("</Project>"); if (defaultTarget.CustomExternalProjectFilePath == null) { // Save the files Utilities.WriteFileIfChanged(project.Path, csProjectFileContent.ToString()); } }
/// <inheritdoc /> public override void GenerateSolution(Solution solution) { // Try to extract info from the existing solution file to make random IDs stable var solutionId = Guid.NewGuid(); var folderIds = new Dictionary <string, Guid>(); if (File.Exists(solution.Path)) { try { var contents = File.ReadAllText(solution.Path); var solutionIdMatch = Regex.Match(contents, "SolutionGuid = \\{(.*?)\\}"); if (solutionIdMatch.Success) { var value = solutionIdMatch.Value; solutionId = Guid.ParseExact(value.Substring(15), "B"); } var folderIdsMatch = Regex.Match(contents, "Project\\(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\"\\) = \"(.*?)\", \"(.*?)\", \"{(.*?)}\""); if (folderIdsMatch.Success) { foreach (Capture capture in folderIdsMatch.Captures) { var value = capture.Value.Substring("Project(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"".Length); var folder = value.Substring(0, value.IndexOf('\"')); var folderId = Guid.ParseExact(value.Substring(folder.Length * 2 + "\", \"".Length + "\", \"".Length, 38), "B"); folderIds["Source\\" + folder] = folderId; } } } catch (Exception ex) { Log.Warning("Failed to restore solution and solution folders identifiers from existing file."); Log.Exception(ex); } } StringBuilder vcSolutionFileContent = new StringBuilder(); var solutionDirectory = Path.GetDirectoryName(solution.Path); var projects = solution.Projects.Cast <VisualStudioProject>().ToArray(); // Header if (Version == VisualStudioVersion.VisualStudio2019) { //vcSolutionFileContent.AppendLine(); vcSolutionFileContent.AppendLine("Microsoft Visual Studio Solution File, Format Version 12.00"); vcSolutionFileContent.AppendLine("# Visual Studio Version 16"); vcSolutionFileContent.AppendLine("VisualStudioVersion = 16.0.28315.86"); vcSolutionFileContent.AppendLine("MinimumVisualStudioVersion = 10.0.40219.1"); } else if (Version == VisualStudioVersion.VisualStudio2017) { //vcSolutionFileContent.AppendLine(); 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 (Version == VisualStudioVersion.VisualStudio2015) { //vcSolutionFileContent.AppendLine(); 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 { throw new Exception("Unsupported solution file format."); } // Solution folders var folderNames = new HashSet <string>(); { // Move projects to subfolders based on group names including subfolders to match the location of the source in the workspace foreach (var project in projects) { var folder = project.GroupName; if (project.SourceDirectories != null && project.SourceDirectories.Count == 1) { var subFolder = Utilities.MakePathRelativeTo(Path.GetDirectoryName(project.SourceDirectories[0]), project.WorkspaceRootPath); if (subFolder.StartsWith("Source\\")) { subFolder = subFolder.Substring(7); } if (subFolder.Length != 0) { if (folder.Length != 0) { folder += '\\'; } folder += subFolder; } } if (string.IsNullOrEmpty(folder)) { continue; } var folderParents = folder.Split('\\'); for (int i = 0; i < folderParents.Length; i++) { var folderPath = folderParents[0]; for (int j = 1; j <= i; j++) { folderPath += '\\' + folderParents[j]; } if (folderNames.Contains(folderPath)) { project.FolderGuid = folderIds[folderPath]; } else { if (!folderIds.TryGetValue(folderPath, out project.FolderGuid)) { project.FolderGuid = Guid.NewGuid(); folderIds.Add(folderPath, project.FolderGuid); } folderNames.Add(folderPath); } } } foreach (var folder in folderNames) { var folderGuid = folderIds[folder].ToString("B").ToUpperInvariant(); var typeGuid = ProjectTypeGuids.ToOption(ProjectTypeGuids.SolutionFolder); var lastSplit = folder.LastIndexOf('\\'); var name = lastSplit != -1 ? folder.Substring(lastSplit + 1) : folder; vcSolutionFileContent.AppendLine(string.Format("Project(\"{0}\") = \"{1}\", \"{2}\", \"{3}\"", typeGuid, name, name, folderGuid)); vcSolutionFileContent.AppendLine("EndProject"); } } // Solution projects foreach (var project in projects) { var projectId = project.ProjectGuid.ToString("B").ToUpperInvariant(); var typeGuid = ProjectTypeGuids.ToOption(project.ProjectTypeGuid); vcSolutionFileContent.AppendLine(string.Format("Project(\"{0}\") = \"{1}\", \"{2}\", \"{3}\"", typeGuid, project.Name, Utilities.MakePathRelativeTo(project.Path, solutionDirectory), projectId)); if (project.Dependencies.Count > 0) { vcSolutionFileContent.AppendLine("\tProjectSection(ProjectDependencies) = postProject"); foreach (var dependency in project.Dependencies.Cast <VisualStudioProject>()) { string dependencyId = dependency.ProjectGuid.ToString("B").ToUpperInvariant(); vcSolutionFileContent.AppendLine("\t\t" + dependencyId + " = " + dependencyId); } vcSolutionFileContent.AppendLine("\tEndProjectSection"); } vcSolutionFileContent.AppendLine("EndProject"); } // Global configuration { vcSolutionFileContent.AppendLine("Global"); // Collect all unique configurations var configurations = new HashSet <SolutionConfiguration>(); foreach (var project in projects) { if (project.Configurations == null || project.Configurations.Count == 0) { throw new Exception("Missing configurations for project " + project.Name); } foreach (var configuration in project.Configurations) { configurations.Add(new SolutionConfiguration(configuration)); } } // Add missing configurations (Visual Studio needs all permutations of configuration/platform pair) var configurationNames = configurations.Select(x => x.Configuration).Distinct().ToArray(); var platformNames = configurations.Select(x => x.Platform).Distinct().ToArray(); foreach (var configurationName in configurationNames) { foreach (var platformName in platformNames) { configurations.Add(new SolutionConfiguration(configurationName, platformName)); } } // Sort configurations var configurationsSorted = new List <SolutionConfiguration>(configurations); configurationsSorted.Sort(); // Global configurations { vcSolutionFileContent.AppendLine(" GlobalSection(SolutionConfigurationPlatforms) = preSolution"); foreach (var configuration in configurationsSorted) { vcSolutionFileContent.AppendLine(" "+ configuration.Name + " = " + configuration.Name); } vcSolutionFileContent.AppendLine(" EndGlobalSection"); } // Per-project configurations mapping { vcSolutionFileContent.AppendLine(" GlobalSection(ProjectConfigurationPlatforms) = postSolution"); foreach (var project in projects) { string projectId = project.ProjectGuid.ToString("B").ToUpperInvariant(); foreach (var configuration in configurationsSorted) { SolutionConfiguration projectConfiguration; bool build = false; int firstFullMatch = -1, firstPlatformMatch = -1; for (int i = 0; i < project.Configurations.Count; i++) { var e = new SolutionConfiguration(project.Configurations[i]); if (e.Name == configuration.Name) { firstFullMatch = i; break; } if (firstPlatformMatch == -1 && e.Platform == configuration.Platform) { firstPlatformMatch = i; } } if (firstFullMatch != -1) { projectConfiguration = configuration; build = solution.MainProject == project || (solution.MainProject == null && project.Name == solution.Name); } else if (firstPlatformMatch != -1) { projectConfiguration = new SolutionConfiguration(project.Configurations[firstPlatformMatch]); } else { projectConfiguration = new SolutionConfiguration(project.Configurations[0]); } vcSolutionFileContent.AppendLine(string.Format(" {0}.{1}.ActiveCfg = {2}", projectId, configuration.Name, projectConfiguration.OriginalName)); if (build) { vcSolutionFileContent.AppendLine(string.Format(" {0}.{1}.Build.0 = {2}", projectId, configuration.Name, projectConfiguration.OriginalName)); } } } vcSolutionFileContent.AppendLine(" EndGlobalSection"); } // Always show solution root node { vcSolutionFileContent.AppendLine(" GlobalSection(SolutionProperties) = preSolution"); vcSolutionFileContent.AppendLine(" HideSolutionNode = FALSE"); vcSolutionFileContent.AppendLine(" EndGlobalSection"); } // Solution directory hierarchy { vcSolutionFileContent.AppendLine(" GlobalSection(NestedProjects) = preSolution"); // Write nested folders hierarchy foreach (var folder in folderNames) { var lastSplit = folder.LastIndexOf('\\'); if (lastSplit != -1) { var folderGuid = folderIds[folder].ToString("B").ToUpperInvariant(); var parentFolder = folder.Substring(0, lastSplit); var parentFolderGuid = folderIds[parentFolder].ToString("B").ToUpperInvariant(); vcSolutionFileContent.AppendLine(string.Format(" {0} = {1}", folderGuid, parentFolderGuid)); } } // Write mapping for projectId - folderId foreach (var project in projects) { if (project.FolderGuid != Guid.Empty) { var projectGuidString = project.ProjectGuid.ToString("B").ToUpperInvariant(); var folderGuidString = project.FolderGuid.ToString("B").ToUpperInvariant(); vcSolutionFileContent.AppendLine(string.Format(" {0} = {1}", projectGuidString, folderGuidString)); } } vcSolutionFileContent.AppendLine(" EndGlobalSection"); } // Solution identifier { vcSolutionFileContent.AppendLine(" GlobalSection(ExtensibilityGlobals) = postSolution"); vcSolutionFileContent.AppendLine(string.Format(" SolutionGuid = {0}", solutionId.ToString("B").ToUpperInvariant())); vcSolutionFileContent.AppendLine(" EndGlobalSection"); } vcSolutionFileContent.AppendLine("EndGlobal"); } // Save the file Utilities.WriteFileIfChanged(solution.Path, vcSolutionFileContent.ToString()); }