File: OutputPathCalculator.cs
Web Access
Project: ..\..\..\test\Microsoft.NET.TestFramework\Microsoft.NET.TestFramework.csproj (Microsoft.NET.TestFramework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
namespace Microsoft.NET.TestFramework
{
    public class OutputPathCalculator
    {
        public string? ProjectPath { get; set; }
        public bool UseArtifactsOutput { get; set; }
        public bool IncludeProjectNameInArtifactsPaths { get; set; }
        public string? ArtifactsPath { get; set; }
        public string? TargetFramework { get; set; }
        public string? TargetFrameworks { get; set; }
 
        public string? RuntimeIdentifier { get; set; }
 
        public bool IsSdkProject { get; set; } = true;
 
        public static OutputPathCalculator FromProject(string projectPath, TestAsset testAsset)
        {
            return FromProject(projectPath, testAsset.TestProject);
        }
 
        public static OutputPathCalculator FromProject(string? projectPath, TestProject? testProject = null)
        {
            string? originalProjectPath = projectPath;
 
            if (!File.Exists(projectPath) && Directory.Exists(projectPath))
            {
                projectPath = Directory.GetFiles(projectPath, "*.*proj").FirstOrDefault();
            }
 
            //  Support passing in the root test path and looking in subfolder specified by testProject
            if (projectPath == null && testProject != null && testProject.Name is not null && originalProjectPath is not null)
            {
                projectPath = Path.Combine(originalProjectPath, testProject.Name);
 
                if (!File.Exists(projectPath) && Directory.Exists(projectPath))
                {
                    projectPath = Directory.GetFiles(projectPath, "*.*proj").FirstOrDefault();
                }
            }
 
            if (projectPath == null)
            {
                throw new ArgumentException($"Test project not found under {projectPath}");
            }
 
            var calculator = new OutputPathCalculator()
            {
                ProjectPath = projectPath,
            };
 
            if (testProject != null)
            {
                calculator.UseArtifactsOutput = testProject.UseArtifactsOutput;
                calculator.IsSdkProject = testProject.IsSdkProject;
                calculator.IncludeProjectNameInArtifactsPaths = true;
 
                if (testProject.TargetFrameworks.Contains(';'))
                {
                    calculator.TargetFrameworks = testProject.TargetFrameworks;
                }
                else
                {
                    calculator.TargetFramework = testProject.TargetFrameworks;
                }
 
                calculator.RuntimeIdentifier = testProject.RuntimeIdentifier;
 
                if (calculator.IncludeProjectNameInArtifactsPaths)
                {
                    string? directoryBuildPropsFile = GetDirectoryBuildPropsPath(projectPath);
                    if (directoryBuildPropsFile == null)
                    {
                        throw new InvalidOperationException("Couldn't find Directory.Build.props for test project " + projectPath);
                    }
                    calculator.ArtifactsPath = Path.Combine(Path.GetDirectoryName(directoryBuildPropsFile) ?? string.Empty, "artifacts");
                }
                else
                {
                    calculator.ArtifactsPath = Path.Combine(Path.GetDirectoryName(projectPath) ?? string.Empty, "artifacts");
                }
            }
            else
            {
                var project = XDocument.Load(projectPath);
 
                if (project.Root is null)
                {
                    throw new InvalidOperationException($"The project file '{projectPath}' does not have a root element.");
                }
 
                var ns = project.Root.Name.Namespace;
 
                var useArtifactsOutputElement = project.Root.Elements(ns + "PropertyGroup").Elements(ns + "UseArtifactsOutput").FirstOrDefault();
                if (useArtifactsOutputElement != null)
                {
                    calculator.UseArtifactsOutput = bool.Parse(useArtifactsOutputElement.Value);
                    if (calculator.UseArtifactsOutput)
                    {
                        calculator.IncludeProjectNameInArtifactsPaths = false;
                        calculator.ArtifactsPath = Path.Combine(Path.GetDirectoryName(projectPath) ?? string.Empty, "artifacts");
                    }
                }
 
                var targetFrameworkElement = project.Root.Elements(ns + "PropertyGroup").Elements(ns + "TargetFramework").FirstOrDefault();
                if (targetFrameworkElement != null)
                {
                    calculator.TargetFramework = targetFrameworkElement.Value;
                }
 
                var targetFrameworksElement = project.Root.Elements(ns + "PropertyGroup").Elements(ns + "TargetFrameworks").FirstOrDefault();
                if (targetFrameworksElement != null)
                {
                    calculator.TargetFrameworks = targetFrameworksElement.Value;
                }
 
                var runtimeIdentifierElement = project.Root.Elements(ns + "PropertyGroup").Elements(ns + "RuntimeIdentifier").FirstOrDefault();
                if (runtimeIdentifierElement != null)
                {
                    calculator.RuntimeIdentifier = runtimeIdentifierElement.Value;
                }
 
                var directoryBuildPropsFile = GetDirectoryBuildPropsPath(projectPath);
                if (directoryBuildPropsFile != null)
                {
                    var dbp = XDocument.Load(directoryBuildPropsFile);
                    if (dbp.Root is null)
                    {
                        throw new InvalidOperationException($"The project file '{directoryBuildPropsFile}' does not have a root element.");
                    }
                    var dbpns = dbp.Root.Name.Namespace;
 
                    var dbpUsesArtifacts = dbp.Root.Elements(dbpns + "PropertyGroup").Elements(dbpns + "UseArtifactsOutput").FirstOrDefault();
                    if (dbpUsesArtifacts != null)
                    {
 
                        calculator.UseArtifactsOutput = bool.Parse(dbpUsesArtifacts.Value);
                        if (calculator.UseArtifactsOutput)
                        {
                            calculator.IncludeProjectNameInArtifactsPaths = true;
                            calculator.ArtifactsPath = Path.Combine(Path.GetDirectoryName(directoryBuildPropsFile) ?? string.Empty, "artifacts");
                        }
                    }
                }
            }
 
            return calculator;
        }
 
        private static string? GetDirectoryBuildPropsPath(string projectPath)
        {
            string? folder = Path.GetDirectoryName(projectPath);
            while (folder != null)
            {
                string directoryBuildPropsFile = Path.Combine(folder, "Directory.Build.props");
                if (File.Exists(directoryBuildPropsFile))
                {
                    return directoryBuildPropsFile;
                }
                folder = Path.GetDirectoryName(folder);
            }
            return null;
        }
 
        public bool IsMultiTargeted()
        {
            return !string.IsNullOrEmpty(TargetFrameworks);
        }
 
        public string GetOutputDirectory(string? targetFramework = null, string configuration = "Debug", string? runtimeIdentifier = "", string? platform = "")
        {
            if (UseArtifactsOutput)
            {
                string pivot = configuration.ToLowerInvariant();
                if (IsMultiTargeted())
                {
                    pivot += "_" + targetFramework ?? TargetFramework;
                }
                if (string.IsNullOrEmpty(runtimeIdentifier))
                {
                    runtimeIdentifier = RuntimeIdentifier;
                }
                if (!string.IsNullOrEmpty(runtimeIdentifier))
                {
                    pivot += "_" + runtimeIdentifier;
                }
 
                if (IncludeProjectNameInArtifactsPaths)
                {
                    return Path.Combine(ArtifactsPath ?? string.Empty, "bin", Path.GetFileNameWithoutExtension(ProjectPath) ?? string.Empty, pivot);
                }
                else
                {
                    return Path.Combine(ArtifactsPath ?? string.Empty, "bin", pivot);
                }
            }
            else
            {
                targetFramework ??= TargetFramework ?? string.Empty;
                configuration ??= string.Empty;
                runtimeIdentifier ??= RuntimeIdentifier ?? string.Empty;
                platform ??= string.Empty;
 
                if (IsSdkProject)
                {
                    string output = Path.Combine(Path.GetDirectoryName(ProjectPath) ?? string.Empty, "bin", platform, configuration, targetFramework, runtimeIdentifier);
                    return output;
                }
                else
                {
                    string output = Path.Combine(Path.GetDirectoryName(ProjectPath) ?? string.Empty, "bin", platform, configuration);
                    return output;
                }
            }
        }
 
        public string GetPublishDirectory(string? targetFramework = null, string configuration = "Debug", string? runtimeIdentifier = "", string? platform = "")
        {
            if (UseArtifactsOutput)
            {
                string pivot = configuration.ToLowerInvariant();
                if (IsMultiTargeted())
                {
                    pivot += "_" + targetFramework ?? TargetFramework;
                }
                if (string.IsNullOrEmpty(runtimeIdentifier))
                {
                    runtimeIdentifier = RuntimeIdentifier;
                }
                if (!string.IsNullOrEmpty(runtimeIdentifier))
                {
                    pivot += "_" + runtimeIdentifier;
                }
 
                if (IncludeProjectNameInArtifactsPaths)
                {
                    return Path.Combine(ArtifactsPath ?? string.Empty, "publish", Path.GetFileNameWithoutExtension(ProjectPath) ?? string.Empty, pivot);
                }
                else
                {
                    return Path.Combine(ArtifactsPath ?? string.Empty, "publish", pivot);
                }
            }
            else
            {
                targetFramework ??= TargetFramework ?? string.Empty;
                configuration ??= string.Empty;
                runtimeIdentifier ??= RuntimeIdentifier ?? string.Empty;
                platform ??= string.Empty;
 
                string output = Path.Combine(Path.GetDirectoryName(ProjectPath) ?? string.Empty, "bin", platform, configuration, targetFramework, runtimeIdentifier, "publish");
                return output;
            }
        }
 
        public string GetIntermediateDirectory(string? targetFramework = null, string configuration = "Debug", string? runtimeIdentifier = "")
        {
            if (UseArtifactsOutput)
            {
                string pivot = configuration.ToLowerInvariant();
                if (IsMultiTargeted())
                {
                    pivot += "_" + targetFramework ?? TargetFramework;
                }
                if (string.IsNullOrEmpty(runtimeIdentifier))
                {
                    runtimeIdentifier = RuntimeIdentifier;
                }
                if (!string.IsNullOrEmpty(runtimeIdentifier))
                {
                    pivot += "_" + runtimeIdentifier;
                }
 
                if (IncludeProjectNameInArtifactsPaths)
                {
                    return Path.Combine(ArtifactsPath ?? string.Empty, "obj", Path.GetFileNameWithoutExtension(ProjectPath) ?? string.Empty, pivot);
                }
                else
                {
                    return Path.Combine(ArtifactsPath ?? string.Empty, "obj", pivot);
                }
 
            }
 
            targetFramework = targetFramework ?? TargetFramework ?? string.Empty;
            configuration = configuration ?? string.Empty;
            runtimeIdentifier = runtimeIdentifier ?? RuntimeIdentifier ?? string.Empty;
 
            string output = Path.Combine(Path.GetDirectoryName(ProjectPath) ?? string.Empty, "obj", configuration, targetFramework, runtimeIdentifier);
            return output;
        }
 
        public string GetPackageDirectory(string configuration = "Debug")
        {
            if (UseArtifactsOutput)
            {
                return Path.Combine(ArtifactsPath ?? string.Empty, "package", configuration.ToLowerInvariant());
            }
            else
            {
                return Path.Combine(Path.GetDirectoryName(ProjectPath) ?? string.Empty, "bin", configuration);
            }
        }
    }
}