File: Commands\InvokeCommand.cs
Web Access
Project: src\src\Tools\dotnet-getdocument\src\dotnet-getdocument.csproj (dotnet-getdocument)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Versioning;
using Microsoft.Extensions.CommandLineUtils;
using Microsoft.Extensions.Tools.Internal;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
 
namespace Microsoft.Extensions.ApiDescription.Tool.Commands;
 
internal sealed class InvokeCommand : HelpCommandBase
{
    private const string InsideManName = "GetDocument.Insider";
 
    private readonly ProjectOptions _projectOptions = new ProjectOptions();
    private IList<string> _args;
 
    public InvokeCommand(IConsole console) : base(console)
    {
    }
 
    public override void Configure(CommandLineApplication command)
    {
        base.Configure(command);
 
        _projectOptions.Configure(command);
        _args = command.RemainingArguments;
    }
 
    protected override void Validate()
    {
        base.Validate();
        _projectOptions.Validate();
    }
 
    protected override int Execute()
    {
        var thisPath = Path.GetFullPath(Path.GetDirectoryName(typeof(InvokeCommand).Assembly.Location));
 
        var projectName = _projectOptions.ProjectName.Value();
        var assemblyPath = _projectOptions.AssemblyPath.Value();
        var targetDirectory = Path.GetDirectoryName(assemblyPath);
 
        string executable = null;
        var cleanupExecutable = false;
        try
        {
            string toolsDirectory;
            var args = new List<string>();
            var targetFramework = new FrameworkName(_projectOptions.TargetFramework.Value());
            switch (targetFramework.Identifier)
            {
                case ".NETFramework":
                    cleanupExecutable = true;
                    toolsDirectory = Path.Combine(
                        thisPath,
                        _projectOptions.Platform.Value() == "x86" ? "net462-x86" : "net462");
 
                    var executableSource = Path.Combine(toolsDirectory, InsideManName + ".exe");
                    executable = Path.Combine(targetDirectory, InsideManName + ".exe");
                    File.Copy(executableSource, executable, overwrite: true);
 
                    var configPath = assemblyPath + ".config";
                    if (File.Exists(configPath))
                    {
                        File.Copy(configPath, executable + ".config", overwrite: true);
                    }
                    break;
 
                case ".NETCoreApp":
                    if (targetFramework.Version < new Version(2, 1))
                    {
                        throw new CommandException(Resources.FormatOldNETCoreAppProject(
                            projectName,
                            targetFramework.Version));
                    }
                    else if (targetFramework.Version >= new Version(7, 0))
                    {
                        toolsDirectory = Path.Combine(thisPath, $"net{targetFramework.Version}");
                    }
                    else
                    {
                        toolsDirectory = Path.Combine(thisPath, "netcoreapp2.1");
                    }
 
                    executable = DotNetMuxer.MuxerPathOrDefault();
 
                    args.Add("exec");
                    args.Add("--depsFile");
                    args.Add(Path.ChangeExtension(assemblyPath, ".deps.json"));
 
                    var projectAssetsFile = _projectOptions.AssetsFile.Value();
                    if (!string.IsNullOrEmpty(projectAssetsFile) && File.Exists(projectAssetsFile))
                    {
                        using var reader = new JsonTextReader(File.OpenText(projectAssetsFile));
                        var projectAssets = JToken.ReadFrom(reader);
                        var packageFolders = projectAssets["packageFolders"]
                            .Children<JProperty>()
                            .Select(p => p.Name);
 
                        foreach (var packageFolder in packageFolders)
                        {
                            args.Add("--additionalProbingPath");
                            args.Add(packageFolder.TrimEnd(Path.DirectorySeparatorChar));
                        }
                    }
 
                    var runtimeConfigPath = Path.ChangeExtension(assemblyPath, ".runtimeconfig.json");
                    if (File.Exists(runtimeConfigPath))
                    {
                        args.Add("--runtimeConfig");
                        args.Add(runtimeConfigPath);
                    }
                    else
                    {
                        var runtimeFrameworkVersion = _projectOptions.RuntimeFrameworkVersion.Value();
                        if (!string.IsNullOrEmpty(runtimeFrameworkVersion))
                        {
                            args.Add("--fx-version");
                            args.Add(runtimeFrameworkVersion);
                        }
                    }
 
                    args.Add(Path.Combine(toolsDirectory, InsideManName + ".dll"));
                    break;
 
                case ".NETStandard":
                    throw new CommandException(Resources.FormatNETStandardProject(projectName));
 
                default:
                    throw new CommandException(
                        Resources.FormatUnsupportedFramework(projectName, targetFramework.Identifier));
            }
 
            args.AddRange(_args);
            args.Add("--assembly");
            args.Add(assemblyPath);
            args.Add("--project");
            args.Add(projectName);
            args.Add("--tools-directory");
            args.Add(toolsDirectory);
 
            if (ReporterExtensions.PrefixOutput)
            {
                args.Add("--prefix-output");
            }
 
            if (IsQuiet)
            {
                args.Add("--quiet");
            }
 
            if (IsVerbose)
            {
                args.Add("--verbose");
            }
 
            return Exe.Run(executable, args, Reporter);
        }
        finally
        {
            if (cleanupExecutable && !string.IsNullOrEmpty(executable))
            {
                // Ignore errors about in-use files. Should still be marked for delete after process cleanup.
                try
                {
                    File.Delete(executable);
                }
                catch (UnauthorizedAccessException)
                {
                }
 
                try
                {
                    File.Delete(executable + ".config");
                }
                catch (UnauthorizedAccessException)
                {
                }
            }
        }
    }
}