in src/dotnet-svcutil/lib/src/Shared/MSBuildProj.cs [167:369]
public static async Task<MSBuildProj> ParseAsync(string projectText, string projectFullPath, ILogger logger, CancellationToken cancellationToken, string tfMoniker = "")
{
using (var safeLogger = await SafeLogger.WriteStartOperationAsync(logger, $"Parsing project {Path.GetFileName(projectFullPath)}").ConfigureAwait(false))
{
projectFullPath = Path.GetFullPath(projectFullPath);
MSBuildProj msbuildProj = new MSBuildProj
{
FileName = Path.GetFileName(projectFullPath),
DirectoryPath = Path.GetDirectoryName(projectFullPath)
};
XDocument projDefinition = XDocument.Parse(projectText);
var msbuildNS = XNamespace.None;
if (projDefinition.Root != null && projDefinition.Root.Name != null)
{
msbuildNS = projDefinition.Root.Name.Namespace;
}
msbuildProj._msbuildNS = msbuildNS;
msbuildProj.ProjectNode = projDefinition.Element(msbuildNS + "Project");
if (msbuildProj.ProjectNode == null)
{
throw new Exception(Shared.Resources.ErrorInvalidProjectFormat);
}
// The user project can declare TargetFramework and/or TargetFrameworks property. If both are provided, TargetFramework wins.
// When TargetFrameworks is provided, the project is built for every entry specified in the TargetFramework property.
IEnumerable<XElement> targetFrameworkElements = GetSubGroupValues(msbuildProj.ProjectNode, msbuildNS, "PropertyGroup", "TargetFramework");
if (targetFrameworkElements.Count() > 0)
{
// If property is specified more than once, MSBuild will resolve it by overwriting it with the last value.
var targetFramework = targetFrameworkElements.Last().Value.Trim().ToLowerInvariant();
if (!string.IsNullOrWhiteSpace(targetFramework))
{
if(TargetFrameworkHelper.IsSupportedFramework(targetFramework, out FrameworkInfo fxInfo))
{
msbuildProj._targetFrameworks.Add(targetFramework);
}
}
}
if (msbuildProj._targetFrameworks.Count == 0)
{
// TargetFramework was not provided, check TargetFrameworks property.
IEnumerable<XElement> targetFrameworksElements = GetSubGroupValues(msbuildProj.ProjectNode, msbuildNS, "PropertyGroup", "TargetFrameworks");
if (targetFrameworksElements.Count() > 0)
{
var targetFrameworks = targetFrameworksElements.Last().Value;
foreach (var targetFx in targetFrameworks.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Select(p => p.Trim()))
{
if (!string.IsNullOrWhiteSpace(targetFx))
{
msbuildProj._targetFrameworks.Add(targetFx);
}
}
}
}
msbuildProj._targetFrameworks.Sort();
msbuildProj._targetFramework = TargetFrameworkHelper.GetBestFitTargetFramework(msbuildProj._targetFrameworks);
if(string.IsNullOrEmpty(msbuildProj._targetFramework))
{
if(!string.IsNullOrEmpty(tfMoniker) && FrameworkInfo.TryParse(tfMoniker, out FrameworkInfo fxInfo))
{
msbuildProj._targetFramework = fxInfo.FullName;
}
else
{
msbuildProj._targetFramework = string.Concat("net", TargetFrameworkHelper.NetCoreVersionReferenceTable.LastOrDefault().Key.ToString());
}
msbuildProj._targetFrameworks.Add(msbuildProj._targetFramework);
}
// Ensure target framework is valid.
FrameworkInfo frameworkInfo = TargetFrameworkHelper.GetValidFrameworkInfo(msbuildProj.TargetFramework);
IEnumerable<XElement> runtimeIdentifierElements = GetSubGroupValues(msbuildProj.ProjectNode, msbuildNS, "PropertyGroup", "RuntimeIdentifier");
if (runtimeIdentifierElements.Count() > 0)
{
msbuildProj.RuntimeIdentifier = runtimeIdentifierElements.Last().Value.Trim();
}
IEnumerable<XElement> packageReferenceElements = GetSubGroupValues(msbuildProj.ProjectNode, msbuildNS, "ItemGroup", "PackageReference");
foreach (XElement reference in packageReferenceElements)
{
if(!TryGetItemIdentity(reference, out var packageName))
continue;
string version = GetItemValue(reference, "Version");
if (!ProjectDependency.IsValidVersion(version))
{
version = "";
}
ProjectDependency packageDep = ProjectDependency.FromPackage(packageName, version);
msbuildProj._dependencies.Add(packageDep);
}
IEnumerable<XElement> toolReferenceElements = GetSubGroupValues(msbuildProj.ProjectNode, msbuildNS, "ItemGroup", "DotNetCliToolReference");
foreach (XElement reference in toolReferenceElements)
{
if (!TryGetItemIdentity(reference, out var packageName))
continue;
string version = GetItemValue(reference, "Version");
ProjectDependency packageDep = ProjectDependency.FromCliTool(packageName, version);
msbuildProj._dependencies.Add(packageDep);
}
IEnumerable<XElement> projectReferenceElements = GetSubGroupValues(msbuildProj.ProjectNode, msbuildNS, "ItemGroup", "ProjectReference");
foreach (XElement reference in projectReferenceElements)
{
string projectPath = GetItemValue(reference, "Include", throwIfMissing: true);
ProjectDependency projectDep = ProjectDependency.FromProject(projectPath);
msbuildProj._dependencies.Add(projectDep);
}
IEnumerable<XElement> binReferenceElements = GetSubGroupValues(msbuildProj.ProjectNode, msbuildNS, "ItemGroup", "Reference");
foreach (XElement reference in binReferenceElements)
{
//Find hint path or path
string binReference = GetItemIdentity(reference);
if (!Path.IsPathRooted(binReference))
{
string fullPath = null;
bool fullPathFound = true;
XElement hintPath = reference.Element("HintPath");
XElement path = reference.Element("Path");
if (path != null)
{
fullPath = path.Value;
}
else if (hintPath != null)
{
fullPath = hintPath.Value;
}
else
{
try
{
fullPath = new FileInfo(Path.Combine(msbuildProj.DirectoryPath, binReference)).FullName;
}
catch
{
}
if (fullPath == null || !File.Exists(fullPath))
{
fullPathFound = false;
// If we're only targeting .NET Core or .NET Standard projects we throw if we can't find the full path to the assembly.
if (!TargetFrameworkHelper.ContainsFullFrameworkTarget(msbuildProj._targetFrameworks))
{
throw new Exception(string.Format(CultureInfo.CurrentCulture, Shared.Resources.ErrorProjectReferenceMissingFilePathFormat, binReference));
}
}
}
if (fullPathFound)
{
if (System.IO.Directory.Exists(fullPath)) // IsDir?
{
fullPath = Path.Combine(fullPath, binReference);
}
else if (Directory.Exists(Path.Combine(msbuildProj.DirectoryPath, fullPath)))
{
fullPath = Path.Combine(msbuildProj.DirectoryPath, fullPath, binReference);
}
binReference = fullPath;
}
}
ProjectDependency projectDep = ProjectDependency.FromAssembly(binReference);
msbuildProj._dependencies.Add(projectDep);
}
// ensure we have a working directory for the ProcessRunner (ProjectPropertyResolver)..
if (!Directory.Exists(msbuildProj.DirectoryPath))
{
Directory.CreateDirectory(msbuildProj.DirectoryPath);
msbuildProj._ownsDirectory = true;
}
var sdkVersion = await ProjectPropertyResolver.GetSdkVersionAsync(msbuildProj.DirectoryPath, logger, cancellationToken).ConfigureAwait(false);
msbuildProj.SdkVersion = sdkVersion ?? string.Empty;
return msbuildProj;
}
}