|
// 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.IO;
using System.Runtime.Versioning;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Microsoft.Build.Tasks.Deployment.Bootstrapper;
using Microsoft.Build.Tasks.Deployment.ManifestUtilities;
using Microsoft.Build.Utilities;
using Constants = Microsoft.Build.Tasks.Deployment.ManifestUtilities.Constants;
#nullable disable
namespace Microsoft.Build.Tasks
{
/// <summary>
/// Generates a bootstrapper for ClickOnce deployment projects.
/// </summary>
[MSBuildMultiThreadableTask]
[SupportedOSPlatform("windows")]
public sealed class GenerateLauncher : TaskExtension, IMultiThreadableTask
{
private const string LAUNCHER_EXE = "Launcher.exe";
private const string ENGINE_PATH = "Engine"; // relative to ClickOnce bootstrapper path
#region Properties
public TaskEnvironment TaskEnvironment { get; set; } = TaskEnvironment.Fallback;
public ITaskItem EntryPoint { get; set; }
public string LauncherPath { get; set; }
public string OutputPath { get; set; }
public string VisualStudioVersion { get; set; }
public string AssemblyName { get; set; }
[Output]
public ITaskItem OutputEntryPoint { get; set; }
#endregion
// MSBuildTask0005 (transitive unsafe API) warnings are currently emitted here due to
// an analyzer limitation around data-flow reachability. See https://github.com/dotnet/msbuild/issues/13867.
public override bool Execute()
{
if (!NativeMethodsShared.IsWindows)
{
Log.LogErrorWithCodeFromResources("General.TaskRequiresWindows", nameof(GenerateLauncher));
return false;
}
if (LauncherPath == null)
{
// Launcher lives next to ClickOnce bootstrapper.
// Use the TaskEnvironment overload of GetDefaultPath so the fallback is the
// project directory rather than the process-wide current directory.
LauncherPath = Path.Combine(
Deployment.Bootstrapper.Util.GetDefaultPath(VisualStudioVersion, TaskEnvironment),
ENGINE_PATH,
LAUNCHER_EXE);
}
if (EntryPoint == null)
{
Log.LogErrorWithCodeFromResources("GenerateLauncher.InvalidInput");
return false;
}
AbsolutePath outputPath = string.IsNullOrEmpty(OutputPath) ? default : TaskEnvironment.GetAbsolutePath(OutputPath);
var launcherBuilder = new LauncherBuilder(LauncherPath, TaskEnvironment);
string entryPointFileName = Path.GetFileName(EntryPoint.ItemSpec);
// If the EntryPoint specified is apphost.exe or singlefilehost.exe, we need to replace the EntryPoint
// with the AssemblyName instead since apphost.exe/singlefilehost.exe is an intermediate file for
// for final published {assemblyname}.exe.
if ((entryPointFileName.Equals(Constants.AppHostExe, StringComparison.InvariantCultureIgnoreCase) ||
entryPointFileName.Equals(Constants.SingleFileHostExe, StringComparison.InvariantCultureIgnoreCase)) &&
!string.IsNullOrEmpty(AssemblyName))
{
entryPointFileName = AssemblyName;
}
BuildResults results = launcherBuilder.Build(entryPointFileName, outputPath);
BuildMessage[] messages = results.Messages;
if (messages != null)
{
foreach (BuildMessage message in messages)
{
switch (message.Severity)
{
case BuildMessageSeverity.Error:
Log.LogError(null, message.HelpCode, message.HelpKeyword, null, 0, 0, 0, 0, message.Message);
break;
case BuildMessageSeverity.Warning:
Log.LogWarning(null, message.HelpCode, message.HelpKeyword, null, 0, 0, 0, 0, message.Message);
break;
case BuildMessageSeverity.Info:
Log.LogMessage(null, message.HelpCode, message.HelpKeyword, null, 0, 0, 0, 0, message.Message);
continue;
}
}
}
string outputEntryPoint = Path.Combine(Path.GetDirectoryName(EntryPoint.ItemSpec), results.KeyFile);
OutputEntryPoint = new TaskItem(outputEntryPoint);
OutputEntryPoint.SetMetadata(ItemMetadataNames.targetPath, results.KeyFile);
return !Log.HasLoggedErrors;
}
}
}
|