Files
FlaxEngine/Source/Tools/Flax.Build/Projects/VisualStudio/CSProjectGenerator.cs

246 lines
14 KiB
C#

// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace Flax.Build.Projects.VisualStudio
{
/// <summary>
/// The Visual Studio project generator for C# projects (.csproj).
/// </summary>
/// <seealso cref="Flax.Build.Projects.VisualStudio.VisualStudioProjectGenerator" />
public class CSProjectGenerator : VisualStudioProjectGenerator
{
/// <inheritdoc />
public CSProjectGenerator(VisualStudioVersion version) : base(version)
{
}
/// <inheritdoc />
public override string ProjectFileExtension => "csproj";
/// <inheritdoc />
public override TargetType? Type => TargetType.DotNet;
/// <inheritdoc />
public override void GenerateProject(Project project, string solutionPath)
{
var csProjectFileContent = new StringBuilder();
var vsProject = (VisualStudioProject)project;
var projectFileToolVersion = ProjectFileToolVersion;
var projectDirectory = Path.GetDirectoryName(project.Path);
var defaultTarget = project.Targets[0];
foreach (var target in project.Targets)
{
// Pick the Editor-related target
if (target.IsEditor)
{
defaultTarget = target;
break;
}
}
var defaultConfiguration = TargetConfiguration.Debug;
var defaultArchitecture = TargetArchitecture.AnyCPU;
var projectTypes = ProjectTypeGuids.ToOption(ProjectTypeGuids.WindowsCSharp);
if (vsProject.CSharp.UseFlaxVS && VisualStudioInstance.HasFlaxVS)
projectTypes = ProjectTypeGuids.ToOption(ProjectTypeGuids.FlaxVS) + ';' + projectTypes;
// Try to reuse the existing project guid from solution file
vsProject.ProjectGuid = GetProjectGuid(solutionPath, vsProject.Name);
if (vsProject.ProjectGuid == Guid.Empty)
vsProject.ProjectGuid = Guid.NewGuid();
// 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.BaseName));
csProjectFileContent.AppendLine(string.Format(" <AssemblyName>{0}.CSharp</AssemblyName>", project.BaseName));
csProjectFileContent.AppendLine(string.Format(" <TargetFrameworkVersion>{0}</TargetFrameworkVersion>", "v4.5.2"));
csProjectFileContent.AppendLine(" <LangVersion>7.3</LangVersion>");
csProjectFileContent.AppendLine(" <FileAlignment>512</FileAlignment>");
csProjectFileContent.AppendLine(" <TargetFrameworkProfile />");
if (Version >= VisualStudioVersion.VisualStudio2022)
csProjectFileContent.AppendLine(" <ResolveNuGetPackages>false</ResolveNuGetPackages>");
csProjectFileContent.AppendLine(" </PropertyGroup>");
// Default configuration
{
var configuration = project.Configurations.First();
foreach (var e in project.Configurations)
{
if (e.Configuration == defaultConfiguration && e.Target == defaultTarget && e.Platform == Platform.BuildTargetPlatform)
{
configuration = e;
break;
}
}
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}|{1}' \">", defaultConfiguration, defaultArchitecture));
csProjectFileContent.AppendLine(" <DebugSymbols>true</DebugSymbols>");
csProjectFileContent.AppendLine(" <DebugType>portable</DebugType>");
csProjectFileContent.AppendLine(string.Format(" <Optimize>{0}</Optimize>", defaultConfiguration == 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.BaseName));
csProjectFileContent.AppendLine(" <UseVSHostingProcess>true</UseVSHostingProcess>");
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.BaseName));
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.BaseName));
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());
}
}
}
}