main
cs 201 lines 7.47 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.BaseBuildTasks
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Runtime.InteropServices;
9 using Microsoft.Build.Utilities;
10
11 public abstract class BaseToolsetTask : ToolTask
12 {
13 /// <summary>
14 /// Gets or sets additional options that are appended the the tool command-line.
15 /// </summary>
16 /// <remarks>
17 /// This allows the task to support extended options in the tool which are not
18 /// explicitly implemented as properties on the task.
19 /// </remarks>
20 public string AdditionalOptions { get; set; }
21
22 /// <summary>
23 /// Gets or sets whether to display the logo.
24 /// </summary>
25 public bool NoLogo { get; set; }
26
27 /// <summary>
28 /// Gets or sets whether all warnings should be suppressed.
29 /// </summary>
30 public bool SuppressAllWarnings { get; set; }
31
32 /// <summary>
33 /// Gets or sets a list of specific warnings to be suppressed.
34 /// </summary>
35 public string[] SuppressSpecificWarnings { get; set; }
36
37 /// <summary>
38 /// Gets or sets whether all warnings should be treated as errors.
39 /// </summary>
40 public bool TreatWarningsAsErrors { get; set; }
41
42 /// <summary>
43 /// Gets or sets a list of specific warnings to treat as errors.
44 /// </summary>
45 public string[] TreatSpecificWarningsAsErrors { get; set; }
46
47 /// <summary>
48 /// Gets or sets whether to display verbose output.
49 /// </summary>
50 public bool VerboseOutput { get; set; }
51
52 /// <summary>
53 /// Get the path to the executable.
54 /// </summary>
55 /// <remarks>
56 /// ToolTask only calls GenerateFullPathToTool when the ToolPath property is not set.
57 /// WiX never sets the ToolPath property, but the user can through $(WixToolDir).
58 /// If we return only a file name, ToolTask will search the system paths for it.
59 /// </remarks>
60 protected sealed override string GenerateFullPathToTool()
61 {
62 var defaultToolFullPath = this.GetDefaultToolFullPath();
63
64 #if NETCOREAPP
65 // If we're pointing at an executable use that.
66 if (IsSelfExecutable(defaultToolFullPath, out var finalToolFullPath))
67 {
68 return finalToolFullPath;
69 }
70
71 // Otherwise, use "dotnet.exe" to run an assembly dll.
72 return Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ?? "dotnet";
73 #else
74 return defaultToolFullPath;
75 #endif
76 }
77
78 /// <summary>
79 /// Builds a command line from options in this and derivative tasks.
80 /// </summary>
81 /// <remarks>
82 /// Derivative classes should call BuildCommandLine() on the base class to ensure that common command line options are added to the command.
83 /// </remarks>
84 protected virtual void BuildCommandLine(WixCommandLineBuilder commandLineBuilder)
85 {
86 commandLineBuilder.AppendIfTrue("-nologo", this.NoLogo);
87 commandLineBuilder.AppendArrayIfNotNull("-sw", this.SuppressSpecificWarnings);
88 commandLineBuilder.AppendIfTrue("-sw", this.SuppressAllWarnings);
89 commandLineBuilder.AppendIfTrue("-v", this.VerboseOutput);
90 commandLineBuilder.AppendArrayIfNotNull("-wx", this.TreatSpecificWarningsAsErrors);
91 commandLineBuilder.AppendIfTrue("-wx", this.TreatWarningsAsErrors);
92 commandLineBuilder.AppendTextIfNotNull(this.AdditionalOptions);
93 }
94
95 protected sealed override string GenerateResponseFileCommands()
96 {
97 var commandLineBuilder = new WixCommandLineBuilder();
98 this.BuildCommandLine(commandLineBuilder);
99 return commandLineBuilder.ToString();
100 }
101
102 #if NETCOREAPP
103 protected override string GenerateCommandLineCommands()
104 {
105 // If the target tool path is an executable, we don't need to add anything to the command-line.
106 var toolFullPath = this.GetToolFullPath();
107
108 if (IsSelfExecutable(toolFullPath, out var finalToolFullPath))
109 {
110 return null;
111 }
112 else // we're using "dotnet.exe" to run the assembly so add "exec" plus path to the command-line.
113 {
114 return $"exec \"{finalToolFullPath}\"";
115 }
116 }
117
118 private static bool IsSelfExecutable(string proposedToolFullPath, out string finalToolFullPath)
119 {
120 var toolFullPathWithoutExtension = Path.Combine(Path.GetDirectoryName(proposedToolFullPath), Path.GetFileNameWithoutExtension(proposedToolFullPath));
121 var exeExtension = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : String.Empty;
122 var exeToolFullPath = $"{toolFullPathWithoutExtension}{exeExtension}";
123 if (File.Exists(exeToolFullPath))
124 {
125 finalToolFullPath = exeToolFullPath;
126 return true;
127 }
128
129 finalToolFullPath = $"{toolFullPathWithoutExtension}.dll";
130 return false;
131 }
132 #else
133 private string FindArchitectureSpecificToolPath(string baseFolder)
134 {
135 var checkedPaths = new List<string>();
136
137 // First try to find a folder that matches this task's architecture.
138 var archFolder = RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant();
139
140 var path = Path.Combine(baseFolder, archFolder, this.ToolExe);
141
142 if (File.Exists(path))
143 {
144 return path;
145 }
146
147 checkedPaths.Add(path);
148
149 // Try to fallback to "x86" folder since it tends to run on all architectures.
150 if (!String.Equals(archFolder, "x86", StringComparison.OrdinalIgnoreCase))
151 {
152 path = Path.Combine(baseFolder, "x86", this.ToolExe);
153
154 if (File.Exists(path))
155 {
156 return path;
157 }
158
159 checkedPaths.Add(path);
160 }
161
162 // Return empty, even though this isn't likely to be there.
163 path = Path.Combine(baseFolder, this.ToolExe);
164
165 if (File.Exists(path))
166 {
167 return path;
168 }
169
170 checkedPaths.Add(path);
171
172 this.Log.LogError("Cannot find tool executable {0} at any of the checked paths: {1}. This is unexpected and will cause later commands to fail.", this.ToolExe, String.Join(", ", checkedPaths));
173
174 return path;
175 }
176 #endif
177
178 private string GetDefaultToolFullPath()
179 {
180 #if NETCOREAPP
181 var thisTaskFolder = Path.GetDirectoryName(Path.GetFullPath(typeof(BaseToolsetTask).Assembly.Location));
182
183 return Path.Combine(thisTaskFolder, this.ToolExe);
184 #else
185 var thisTaskFolder = Path.GetDirectoryName(Path.GetFullPath(new Uri(typeof(BaseToolsetTask).Assembly.CodeBase).LocalPath));
186
187 return this.FindArchitectureSpecificToolPath(thisTaskFolder);
188 #endif
189 }
190
191 private string GetToolFullPath()
192 {
193 if (String.IsNullOrEmpty(this.ToolPath))
194 {
195 return this.GetDefaultToolFullPath();
196 }
197
198 return Path.Combine(this.ToolPath, this.ToolExe);
199 }
200 }
201 }