File: AxTlbBaseTask.cs
Web Access
Project: ..\..\..\src\Tasks\Microsoft.Build.Tasks.csproj (Microsoft.Build.Tasks.Core)
// 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.Reflection;
using Microsoft.Build.Shared.FileSystem;
 
#nullable disable
 
namespace Microsoft.Build.Tasks
{
    /// <summary>
    /// ToolTask that contains shared functionality between the AxImp and TlbImp tasks.
    /// </summary>
    internal abstract class AxTlbBaseTask : ToolTaskExtension
    {
        #region Private Data
 
        /// <summary>
        /// True if the keyfile only contains the public key data, and thus
        /// we should pass the file using the /publickey: parameter instead of
        /// /keyfile.
        /// </summary>
        private bool _delaySigningAndKeyFileOnlyContainsPublicKey;
 
        #endregion
 
        #region Properties
        /// <summary>
        /// Force strong name delay signing.  Used with KeyFile or KeyContainer.
        /// </summary>
        public bool DelaySign
        {
            get => GetBoolParameterWithDefault(nameof(DelaySign), false);
            set => Bag[nameof(DelaySign)] = value;
        }
 
        /// <summary>
        /// Key container containing strong name key pair.
        /// </summary>
        public string KeyContainer
        {
            get => (string)Bag[nameof(KeyContainer)];
            set => Bag[nameof(KeyContainer)] = value;
        }
 
        /// <summary>
        /// File containing strong name key pair.
        /// </summary>
        public string KeyFile
        {
            get => (string)Bag[nameof(KeyFile)];
            set => Bag[nameof(KeyFile)] = value;
        }
 
        /// <summary>
        /// Path to the SDK directory where AxImp.exe and TlbImp.exe can be found
        /// </summary>
        public string SdkToolsPath
        {
            get => (string)Bag[nameof(SdkToolsPath)];
            set => Bag[nameof(SdkToolsPath)] = value;
        }
 
        #endregion // Properties
 
        #region ToolTask Members
 
        /// <summary>
        /// Returns the name of the tool to execute.  AxTlbBaseTask is not
        /// executable, so return null for the ToolName -- And make sure that
        /// Execute() logs an error!
        /// </summary>
        protected override string ToolName { get; } = null;
 
        /// <summary>
        /// Invokes the ToolTask with the given parameters
        /// </summary>
        /// <returns>True if the task succeeded, false otherwise</returns>
        public override bool Execute()
        {
            // This is not a callable task on its own -- so need to make sure that
            // only descendant tasks who have defined their ToolName can be executed
            if (String.IsNullOrEmpty(ToolName))
            {
                Log.LogErrorWithCodeFromResources("AxTlbBaseTask.ToolNameMustBeSet");
                return false;
            }
 
            return base.Execute();
        }
 
        /// <summary>
        /// Adds commands for the tool being executed, that cannot be put in a response file.
        /// </summary>
        /// <param name="commandLine">The CommandLineBuilderExtension to add the commands to</param>
        protected internal override void AddCommandLineCommands(CommandLineBuilderExtension commandLine)
        {
            AddStrongNameOptions(commandLine);
            base.AddCommandLineCommands(commandLine);
        }
 
        /// <summary>
        /// Generates the full path to the tool being executed by this ToolTask
        /// </summary>
        /// <returns>A string containing the full path of this tool, or null if the tool was not found</returns>
        protected override string GenerateFullPathToTool()
        {
            string pathToTool = SdkToolsPathUtility.GeneratePathToTool(
                SdkToolsPathUtility.FileInfoExists,
                Utilities.ProcessorArchitecture.CurrentProcessArchitecture,
                SdkToolsPath,
                ToolName,
                Log,
                true);
 
            return pathToTool;
        }
 
        /// <summary>
        /// Validates the parameters passed to the task
        /// </summary>
        /// <returns>True if parameters are valid</returns>
        protected override bool ValidateParameters()
        {
            // Verify that a path for the tool exists -- if the tool doesn't exist in it
            // we'll worry about that later
            if ((String.IsNullOrEmpty(ToolPath) || !FileSystems.Default.DirectoryExists(ToolPath)) &&
                (String.IsNullOrEmpty(SdkToolsPath) || !FileSystems.Default.DirectoryExists(SdkToolsPath)))
            {
                Log.LogErrorWithCodeFromResources("AxTlbBaseTask.SdkOrToolPathNotSpecifiedOrInvalid", SdkToolsPath ?? "", ToolPath ?? "");
                return false;
            }
 
            if (ValidateStrongNameParameters())
            {
                // Allow the base class to do any validation it thinks necessary -- as far
                // as we're concerned, parameters check out properly
                return base.ValidateParameters();
            }
            return false;
        }
 
        /// <summary>
        /// Adds options involving strong name signing -- syntax is the same between
        /// AxImp and TlbImp
        /// </summary>
        /// <param name="commandLine">The command line to add options to</param>
        private void AddStrongNameOptions(CommandLineBuilderExtension commandLine)
        {
            commandLine.AppendWhenTrue("/delaysign", Bag, "DelaySign");
 
            // If we're delay-signing, we only need the public key, but if we use the /publickey
            // switch, it will consume the entire key file, assume that's just the public key, and
            // throw an error.
            //
            // So use /publickey if that's all our KeyFile contains, but KeyFile otherwise.
            if (_delaySigningAndKeyFileOnlyContainsPublicKey)
            {
                commandLine.AppendSwitchIfNotNull("/publickey:", KeyFile);
            }
            else
            {
                commandLine.AppendSwitchIfNotNull("/keyfile:", KeyFile);
            }
 
            commandLine.AppendSwitchIfNotNull("/keycontainer:", KeyContainer);
        }
 
        /// <summary>
        /// Validates the parameters passed to the task that involve strong name signing --
        /// DelaySign, KeyContainer, and KeyFile
        /// </summary>
        /// <returns>true if the parameters are valid, false otherwise.</returns>
        private bool ValidateStrongNameParameters()
        {
            bool keyFileExists = false;
 
            // Make sure that if KeyFile is defined, it's a real file.
            if (!String.IsNullOrEmpty(KeyFile))
            {
                if (FileSystems.Default.FileExists(KeyFile))
                {
                    keyFileExists = true;
                }
                else
                {
                    Log.LogErrorWithCodeFromResources("AxTlbBaseTask.InvalidKeyFileSpecified", KeyFile);
                    return false;
                }
            }
 
            // Check if KeyContainer name is specified
            bool keyContainerSpecified = !String.IsNullOrEmpty(KeyContainer);
 
            // Cannot define both KeyFile and KeyContainer
            if (keyFileExists && keyContainerSpecified)
            {
                Log.LogErrorWithCodeFromResources("AxTlbBaseTask.CannotSpecifyBothKeyFileAndKeyContainer");
                return false;
            }
 
            // If this assembly is delay signed, either KeyFile or KeyContainer must be defined
            if (DelaySign && !keyFileExists && !keyContainerSpecified)
            {
                Log.LogErrorWithCodeFromResources("AxTlbBaseTask.CannotSpecifyDelaySignWithoutEitherKeyFileOrKeyContainer");
                return false;
            }
 
            // If KeyFile or KeyContainer is specified, verify that a key pair exists (or if delay-signed,
            // even just a public key)
            if (keyFileExists || keyContainerSpecified)
            {
                StrongNameKeyPair keyPair;
                byte[] publicKey = null;
 
                try
                {
                    StrongNameUtils.GetStrongNameKey(Log, KeyFile, KeyContainer, out keyPair, out publicKey);
                }
                catch (StrongNameException e)
                {
                    Log.LogErrorFromException(e);
                    keyPair = null;
 
                    // don't return here -- let the appropriate error below get logged also.
                }
 
                if (DelaySign)
                {
                    if (publicKey == null)
                    {
                        Log.LogErrorWithCodeFromResources("AxTlbBaseTask.StrongNameUtils.NoPublicKeySpecified");
                        return false;
                    }
                    if (keyPair == null)
                    {
                        // record this so we know which switch to pass to the task
                        _delaySigningAndKeyFileOnlyContainsPublicKey = true;
                    }
                }
                else
                {
                    if (keyPair == null)
                    {
                        if (!String.IsNullOrEmpty(KeyContainer))
                        {
                            Log.LogErrorWithCodeFromResources("AxTlbBaseTask.StrongNameUtils.NoKeyPairInContainer", KeyContainer);
                            return false;
                        }
                        if (!String.IsNullOrEmpty(KeyFile))
                        {
                            Log.LogErrorWithCodeFromResources("AxTlbBaseTask.StrongNameUtils.NoKeyPairInFile", KeyFile);
                            return false;
                        }
                    }
                }
            }
 
            return true;
        }
 
        #endregion // ToolTask Members
    }
}