Full support for multitargeting project references in a .wixproj
Closes 7241
Rob Mensching committed
Feb 14, 2023 at 23:11 UTC
69f11ee0275692528ed034a3885fa9f0c1504704
16 files changed
+834
-107
src/wix/WixToolset.BuildTasks/CreateProjectReferenceDefineConstantsAndBindPaths.cs
+7
-7
@@ -31,7 +31,7 @@ namespace WixToolset.BuildTasks
31
public override bool Execute()
32
{
33
var bindPaths = new Dictionary<string, List<ITaskItem>>(StringComparer.OrdinalIgnoreCase);
34
- var defineConstants = new Dictionary<string, string>();
34
+ var defineConstants = new SortedDictionary<string, string>();
35
36
foreach (var resolvedReference in this.ResolvedProjectReferences)
37
{
@@ -46,7 +46,7 @@ namespace WixToolset.BuildTasks
46
return true;
47
}
48
49
- private void AddBindPathsForResolvedReference(Dictionary<string, List<ITaskItem>> bindPathByPaths, ITaskItem resolvedReference)
49
+ private void AddBindPathsForResolvedReference(IDictionary<string, List<ITaskItem>> bindPathByPaths, ITaskItem resolvedReference)
50
{
51
// If the BindName was not explicitly provided, try to use the source project's filename
52
// as the bind name.
@@ -84,7 +84,7 @@ namespace WixToolset.BuildTasks
84
}
85
}
86
87
- private void AddDefineConstantsForResolvedReference(Dictionary<string, string> defineConstants, ITaskItem resolvedReference)
87
+ private void AddDefineConstantsForResolvedReference(IDictionary<string, string> defineConstants, ITaskItem resolvedReference)
88
{
89
var configuration = resolvedReference.GetMetadata("Configuration");
90
var fullConfiguration = resolvedReference.GetMetadata("FullConfiguration");
@@ -96,7 +96,7 @@ namespace WixToolset.BuildTasks
96
var projectFileName = Path.GetFileName(projectPath);
97
var projectName = Path.GetFileNameWithoutExtension(projectPath);
98
99
- var referenceName = ToolsCommon.GetIdentifierFromName(ToolsCommon.GetMetadataOrDefault(resolvedReference, "Name", projectName));
99
+ var referenceName = ToolsCommon.CreateIdentifierFromValue(ToolsCommon.GetMetadataOrDefault(resolvedReference, "Name", projectName));
100
101
var targetPath = resolvedReference.GetMetadata("FullPath");
102
var targetDir = Path.GetDirectoryName(targetPath) + Path.DirectorySeparatorChar;
@@ -156,12 +156,12 @@ namespace WixToolset.BuildTasks
156
}
157
158
// If there was only one targetpath we need to create its culture specific define
159
- if (!oldTargetPath.Contains(";"))
159
+ if (!oldTargetPath.Contains("%3B"))
160
{
161
var oldSubFolder = FindSubfolder(oldTargetPath, targetDir, targetFileName);
162
if (!String.IsNullOrEmpty(oldSubFolder))
163
{
164
- defineConstants[referenceName + "." + oldSubFolder.Replace('\\', '_') + ".TargetPath"] = oldTargetPath;
164
+ defineConstants[referenceName + "." + ToolsCommon.CreateIdentifierFromValue(oldSubFolder) + ".TargetPath"] = oldTargetPath;
165
}
166
}
167
@@ -169,7 +169,7 @@ namespace WixToolset.BuildTasks
169
var subFolder = FindSubfolder(targetPath, targetDir, targetFileName);
170
if (!String.IsNullOrEmpty(subFolder))
171
{
172
- defineConstants[referenceName + "." + subFolder.Replace('\\', '_') + ".TargetPath"] = targetPath;
172
+ defineConstants[referenceName + "." + ToolsCommon.CreateIdentifierFromValue(subFolder) + ".TargetPath"] = targetPath;
173
}
174
}
175
else
src/wix/WixToolset.BuildTasks/MetadataValue.cs
new
+68
@@ -0,0 +1,68 @@
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.BuildTasks
4
+{
5
+ using System;
6
+ using Microsoft.Build.Framework;
7
+
8
+ internal class MetadataValue
9
+ {
10
+ public MetadataValue(ITaskItem item, string name, string valuePrefix = null, string defaultValue = "")
11
+ {
12
+ this.Item = item;
13
+ this.Name = name;
14
+
15
+ var value = item.GetMetadata(name);
16
+
17
+ this.HadValue = !String.IsNullOrWhiteSpace(value);
18
+ this.OriginalValue = value;
19
+
20
+ if (!this.HadValue)
21
+ {
22
+ this.Value = defaultValue;
23
+ this.ValidValue = true;
24
+ }
25
+ else if (String.IsNullOrWhiteSpace(valuePrefix))
26
+ {
27
+ this.Value = value;
28
+ this.ValidValue = true;
29
+ }
30
+ else if (value.StartsWith(valuePrefix) && value.Length > valuePrefix.Length)
31
+ {
32
+ this.Value = value.Substring(valuePrefix.Length);
33
+ this.ValidValue = true;
34
+ }
35
+ }
36
+
37
+ public ITaskItem Item { get; }
38
+
39
+ public string Name { get; }
40
+
41
+ public bool HadValue { get; }
42
+
43
+ public bool Modified => !String.Equals(this.OriginalValue, this.Value, StringComparison.Ordinal);
44
+
45
+ public string OriginalValue { get; }
46
+
47
+ public string Value { get; private set; }
48
+
49
+ public bool ValidValue { get; }
50
+
51
+ public void SetValue(string value)
52
+ {
53
+ this.Value = value;
54
+ }
55
+
56
+ public void Apply()
57
+ {
58
+ if (String.IsNullOrWhiteSpace(this.Value))
59
+ {
60
+ this.Item.RemoveMetadata(this.Name);
61
+ }
62
+ else
63
+ {
64
+ this.Item.SetMetadata(this.Name, this.Value);
65
+ }
66
+ }
67
+ }
68
+}
src/wix/WixToolset.BuildTasks/MetadataValueList.cs
new
+100
@@ -0,0 +1,100 @@
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.BuildTasks
4
+{
5
+ using System;
6
+ using System.Collections.Generic;
7
+ using System.Linq;
8
+ using Microsoft.Build.Framework;
9
+
10
+ internal class MetadataValueList
11
+ {
12
+ private static readonly char[] MetadataListSplitter = new char[] { ',', ';' };
13
+
14
+ public MetadataValueList(ITaskItem item, string name)
15
+ {
16
+ this.Item = item;
17
+ this.Name = name;
18
+
19
+ var value = item.GetMetadata(name);
20
+
21
+ this.HadValue = !String.IsNullOrWhiteSpace(value);
22
+ this.OriginalValue = value;
23
+
24
+ this.Values = value.Split(MetadataListSplitter).Where(s => !String.IsNullOrWhiteSpace(s)).ToList();
25
+ }
26
+
27
+ public ITaskItem Item { get; }
28
+
29
+ public string Name { get; }
30
+
31
+ public bool HadValue { get; }
32
+
33
+ public string OriginalValue { get; }
34
+
35
+ public bool Modified { get; private set; }
36
+
37
+ public List<string> Values { get; }
38
+
39
+ public void Clear()
40
+ {
41
+ if (this.Values.Count > 0)
42
+ {
43
+ this.Modified = true;
44
+ this.Values.Clear();
45
+ }
46
+ }
47
+
48
+ public void SetValue(string prefix, string value)
49
+ {
50
+ if (!String.IsNullOrEmpty(prefix))
51
+ {
52
+ value = String.IsNullOrWhiteSpace(value) ? null : prefix + value;
53
+
54
+ for (var i = 0; i < this.Values.Count; ++i)
55
+ {
56
+ if (this.Values[i].StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
57
+ {
58
+ if (value == null)
59
+ {
60
+ this.Values.RemoveAt(i);
61
+ }
62
+ else
63
+ {
64
+ this.Values[i] = value;
65
+ }
66
+
67
+ this.Modified = true;
68
+ return;
69
+ }
70
+ }
71
+ }
72
+
73
+ if (!String.IsNullOrWhiteSpace(value) && !this.Values.Contains(value))
74
+ {
75
+ this.Modified = true;
76
+ this.Values.Add(value);
77
+ }
78
+ }
79
+
80
+ public void AddRange(IEnumerable<string> values)
81
+ {
82
+ foreach (var value in values)
83
+ {
84
+ this.SetValue(null, value);
85
+ }
86
+ }
87
+
88
+ public void Apply()
89
+ {
90
+ if (this.Values.Count == 0)
91
+ {
92
+ this.Item.RemoveMetadata(this.Name);
93
+ }
94
+ else
95
+ {
96
+ this.Item.SetMetadata(this.Name, String.Join(";", this.Values));
97
+ }
98
+ }
99
+ }
100
+}
src/wix/WixToolset.BuildTasks/ToolsCommon.cs
renamed
+6
-7
@@ -18,18 +18,17 @@ namespace WixToolset.BuildTasks
18
private static readonly Regex IllegalIdentifierCharacters = new Regex(@"[^A-Za-z0-9_\.]|\.{2,}"); // non 'words' and assorted valid characters
19
20
/// <summary>
21
- /// Return an identifier based on passed file/directory name
21
+ /// Return an identifier based on passed value.
22
/// </summary>
23
- /// <param name="name">File/directory name to generate identifer from</param>
24
- /// <returns>A version of the name that is a legal identifier.</returns>
25
- /// <remarks>This is duplicated from WiX's Common class.</remarks>
26
- public static string GetIdentifierFromName(string name)
23
+ /// <param name="value">Value to create identifer from.</param>
24
+ /// <returns>A version of the value that is a legal identifier.</returns>
25
+ public static string CreateIdentifierFromValue(string value)
26
{
28
- var result = IllegalIdentifierCharacters.Replace(name, "_"); // replace illegal characters with "_".
27
+ var result = IllegalIdentifierCharacters.Replace(value, "_"); // replace illegal characters with "_".
28
29
// MSI identifiers must begin with an alphabetic character or an
30
// underscore. Prefix all other values with an underscore.
32
- if (AddPrefix.IsMatch(name))
31
+ if (AddPrefix.IsMatch(value))
32
{
33
result = String.Concat("_", result);
34
}
src/wix/WixToolset.BuildTasks/UpdateProjectReferenceMetadata.cs
+374
-92
@@ -6,15 +6,16 @@ namespace WixToolset.BuildTasks
6
using System.Collections.Generic;
7
using System.IO;
8
using System.Linq;
9
+ using System.Text;
10
using Microsoft.Build.Framework;
11
using Microsoft.Build.Utilities;
12
13
/// <summary>
13
- /// This task adds publish metadata to the appropriate project references.
14
+ /// This task adds mutli-targeting and publish metadata to the appropriate project references.
15
/// </summary>
16
public class UpdateProjectReferenceMetadata : Task
17
{
17
- private static readonly char[] TargetFrameworksSplitter = new char[] { ',', ';' };
18
+ private static readonly char[] MetadataPairSplitter = new char[] { '|' };
19
20
/// <summary>
21
/// The list of project references that exist.
@@ -29,168 +30,449 @@ namespace WixToolset.BuildTasks
30
public ITaskItem[] UpdatedProjectReferences { get; private set; }
31
32
/// <summary>
32
- /// Finds all project references requesting publishing and updates them to publish instead of build and
33
+ /// Finds all project references requesting multi-targeting and publishing and updates them to publish instead of build and
34
/// sets target framework if requested.
35
/// </summary>
36
/// <returns>True upon completion of the task execution.</returns>
37
public override bool Execute()
38
{
38
- var updatedProjectReferences = new List<ITaskItem>();
39
var intermediateFolder = Path.GetFullPath(this.IntermediateFolder);
40
41
- foreach (var projectReference in this.ProjectReferences)
42
- {
43
- var additionalProjectReferences = new List<ITaskItem>();
41
+ // Create the project reference facades.
42
+ var projectReferenceFacades = this.ProjectReferences.Select(p => ProjectReferenceFacade.CreateFacade(p, this.Log, intermediateFolder));
43
+
44
+ // Expand the facade count by applying Configurations/Platforms.
45
+ projectReferenceFacades = this.ExpandProjectReferencesForConfigurationsAndPlatforms(projectReferenceFacades);
46
+
47
+ // Expand the facade count by applying TargetFrameworks/RuntimeIdentifiers.
48
+ projectReferenceFacades = this.ExpandProjectReferencesForTargetFrameworksAndRuntimeIdentifiers(projectReferenceFacades);
49
45
- var updatedProjectReference = this.TrySetTargetFrameworksOnProjectReference(projectReference, additionalProjectReferences);
50
+ // Assign any metadata added during expansion above to the project references.
51
+ this.UpdatedProjectReferences = this.AssignMetadataToProjectReferences(projectReferenceFacades).ToArray();
52
+
53
+ return true;
54
+ }
55
47
- if (this.TryAddPublishPropertiesToProjectReference(projectReference, intermediateFolder))
56
+ private IEnumerable<ProjectReferenceFacade> ExpandProjectReferencesForConfigurationsAndPlatforms(IEnumerable<ProjectReferenceFacade> projectReferenceFacades)
57
+ {
58
+ foreach (var projectReferenceFacade in projectReferenceFacades)
59
+ {
60
+ var configurationsWithPlatforms = ExpandTerms(projectReferenceFacade.AvailableConfigurations, projectReferenceFacade.AvailablePlatforms).ToList();
61
+
62
+ if (configurationsWithPlatforms.Count == 0)
63
+ {
64
+ yield return projectReferenceFacade;
65
+ }
66
+ else
67
{
49
- foreach (var additionalProjectReference in additionalProjectReferences)
68
+ var expand = new List<ProjectReferenceFacade>(configurationsWithPlatforms.Count)
69
{
51
- this.TryAddPublishPropertiesToProjectReference(additionalProjectReference, intermediateFolder);
70
+ projectReferenceFacade
71
+ };
72
+
73
+ // First, clone the project reference so there are enough facades for all of the
74
+ // requested configurations/platforms.
75
+ for (var i = 1; i < configurationsWithPlatforms.Count; ++i)
76
+ {
77
+ expand.Add(projectReferenceFacade.Clone());
78
}
79
54
- updatedProjectReference = true;
80
+ // Then set the configuration/platform on each project reference.
81
+ for (var i = 0; i < configurationsWithPlatforms.Count; ++i)
82
+ {
83
+ expand[i].Configuration = configurationsWithPlatforms[i].FirstTerm;
84
+ expand[i].Platform = configurationsWithPlatforms[i].SecondTerm;
85
+
86
+ yield return expand[i];
87
+ }
88
}
89
+ }
90
+ }
91
+
92
+ private IEnumerable<ProjectReferenceFacade> ExpandProjectReferencesForTargetFrameworksAndRuntimeIdentifiers(IEnumerable<ProjectReferenceFacade> projectReferenceFacades)
93
+ {
94
+ foreach (var projectReferenceFacade in projectReferenceFacades)
95
+ {
96
+ var tfmsWithRids = ExpandTerms(projectReferenceFacade.AvailableTargetFrameworks, projectReferenceFacade.AvailableRuntimeIdentifiers).ToList();
97
57
- if (updatedProjectReference)
98
+ if (tfmsWithRids.Count == 0)
99
{
59
- updatedProjectReferences.Add(projectReference);
100
+ yield return projectReferenceFacade;
101
}
102
+ else
103
+ {
104
+ var expand = new List<ProjectReferenceFacade>(tfmsWithRids.Count)
105
+ {
106
+ projectReferenceFacade
107
+ };
108
62
- updatedProjectReferences.AddRange(additionalProjectReferences);
63
- }
109
+ // First, clone the project reference so there are enough facades for all of the
110
+ // requested target frameworks/runtime identifiers.
111
+ for (var i = 1; i < tfmsWithRids.Count; ++i)
112
+ {
113
+ expand.Add(projectReferenceFacade.Clone());
114
+ }
115
65
- this.UpdatedProjectReferences = updatedProjectReferences.ToArray();
116
+ // Then set the target framework/runtime identifier on each project reference.
117
+ for (var i = 0; i < tfmsWithRids.Count; ++i)
118
+ {
119
+ expand[i].TargetFramework = tfmsWithRids[i].FirstTerm;
120
+ expand[i].RuntimeIdentifier = tfmsWithRids[i].SecondTerm;
121
67
- return true;
122
+ yield return expand[i];
123
+ }
124
+ }
125
+ }
126
}
127
70
- private bool TryAddPublishPropertiesToProjectReference(ITaskItem projectReference, string intermediateFolder)
128
+ private IEnumerable<ITaskItem> AssignMetadataToProjectReferences(IEnumerable<ProjectReferenceFacade> facades)
129
{
72
- var publish = projectReference.GetMetadata("Publish");
73
- var publishDir = projectReference.GetMetadata("PublishDir");
74
-
75
- if (publish.Equals("true", StringComparison.OrdinalIgnoreCase) ||
76
- (String.IsNullOrWhiteSpace(publish) && !String.IsNullOrWhiteSpace(publishDir)))
130
+ foreach (var facade in facades)
131
{
78
- if (String.IsNullOrWhiteSpace(publishDir))
132
+ var projectReference = facade.ProjectReference;
133
+ var targetsValue = new MetadataValueList(projectReference, "Targets");
134
+
135
+ if (facade.Modified)
136
{
80
- publishDir = CalculatePublishDirFromProjectReference(projectReference, intermediateFolder);
137
+ var configurationValue = new MetadataValue(projectReference, "Configuration");
138
+ var platformValue = new MetadataValue(projectReference, "Platform");
139
+ var fullConfigurationValue = new MetadataValue(projectReference, "FullConfiguration");
140
+ var additionalProperties = new MetadataValueList(projectReference, "AdditionalProperties");
141
+ var bindName = new MetadataValue(projectReference, "BindName");
142
+ var bindPath = new MetadataValue(projectReference, "BindPath");
143
+
144
+ var publishDir = facade.CalculatePublishDir();
145
+
146
+ additionalProperties.SetValue("PublishDir=", publishDir);
147
+ additionalProperties.SetValue("RuntimeIdentifier=", facade.RuntimeIdentifier);
148
+
149
+ additionalProperties.Apply();
150
+
151
+ if (!String.IsNullOrWhiteSpace(facade.Configuration))
152
+ {
153
+ projectReference.SetMetadata("SetConfiguration", $"Configuration={facade.Configuration}");
154
+
155
+ if (configurationValue.HadValue)
156
+ {
157
+ configurationValue.SetValue(facade.Configuration);
158
+ configurationValue.Apply();
159
+ }
160
+ }
161
+
162
+ if (!String.IsNullOrWhiteSpace(facade.Platform))
163
+ {
164
+ projectReference.SetMetadata("SetPlatform", $"Platform={facade.Platform}");
165
+
166
+ if (platformValue.HadValue)
167
+ {
168
+ platformValue.SetValue(facade.Platform);
169
+ platformValue.Apply();
170
+ }
171
+ }
172
+
173
+ if (fullConfigurationValue.HadValue && (configurationValue.Modified || platformValue.Modified))
174
+ {
175
+ fullConfigurationValue.SetValue($"{configurationValue.Value}|{platformValue.Value}");
176
+ fullConfigurationValue.Apply();
177
+ }
178
+
179
+ if (!String.IsNullOrWhiteSpace(facade.TargetFramework))
180
+ {
181
+ projectReference.SetMetadata("SetTargetFramework", $"TargetFramework={facade.TargetFramework}");
182
+ }
183
+
184
+ var bindNameSuffix = facade.CalculateBindNameSuffix();
185
+ if (!String.IsNullOrWhiteSpace(bindNameSuffix))
186
+ {
187
+ var bindNamePrefix = bindName.HadValue ? bindName.Value : Path.GetFileNameWithoutExtension(projectReference.ItemSpec);
188
+
189
+ bindName.SetValue(ToolsCommon.CreateIdentifierFromValue(bindNamePrefix + bindNameSuffix));
190
+ bindName.Apply();
191
+ }
192
+
193
+ if (!bindPath.HadValue)
194
+ {
195
+ bindPath.SetValue(publishDir);
196
+ bindPath.Apply();
197
+ }
198
}
199
83
- publishDir = AppendTargetFrameworkFromProjectReference(projectReference, publishDir);
200
+ if (facade.Publish)
201
+ {
202
+ var publishTargets = new MetadataValueList(projectReference, "PublishTargets");
203
+
204
+ if (publishTargets.HadValue)
205
+ {
206
+ targetsValue.AddRange(publishTargets.Values);
207
+ }
208
+ else
209
+ {
210
+ targetsValue.SetValue(null, "Publish");
211
+ }
212
+
213
+ // GetTargetPath target always needs to be last so we can set bind paths to the output location of the project reference.
214
+ if (targetsValue.Values.Count == 0 || !"GetTargetPath".Equals(targetsValue.Values[targetsValue.Values.Count - 1], StringComparison.OrdinalIgnoreCase))
215
+ {
216
+ targetsValue.Values.Remove("GetTargetPath");
217
+ targetsValue.SetValue(null, "GetTargetPath");
218
+ }
219
+ }
220
85
- publishDir = Path.GetFullPath(publishDir);
221
+ if (targetsValue.Modified)
222
+ {
223
+ targetsValue.Apply();
224
+ }
225
87
- this.AddPublishPropertiesToProjectReference(projectReference, publishDir);
226
+ this.Log.LogMessage(MessageImportance.Low, "Adding metadata to project reference {0} Targets {1}, BindPath {2}={3}, AdditionalProperties: {4}",
227
+ projectReference.ItemSpec, projectReference.GetMetadata("Targets"), projectReference.GetMetadata("BindName"), projectReference.GetMetadata("BindPath"), projectReference.GetMetadata("AdditionalProperties"));
228
89
- return true;
229
+ yield return projectReference;
230
}
91
-
92
- return false;
231
}
232
95
- private bool TrySetTargetFrameworksOnProjectReference(ITaskItem projectReference, List<ITaskItem> additionalProjectReferences)
233
+ private static IEnumerable<ExpansionTerms> ExpandTerms(IReadOnlyCollection<string> firstTerms, IReadOnlyCollection<string> secondTerms)
234
{
97
- var setTargetFramework = projectReference.GetMetadata("SetTargetFramework");
98
- var targetFrameworks = projectReference.GetMetadata("TargetFrameworks");
99
- var targetFrameworksToSet = targetFrameworks.Split(TargetFrameworksSplitter).Where(s => !String.IsNullOrWhiteSpace(s)).ToList();
235
+ if (firstTerms.Count == 0)
236
+ {
237
+ firstTerms = new[] { String.Empty };
238
+ }
239
101
- if (String.IsNullOrWhiteSpace(setTargetFramework) && targetFrameworksToSet.Count > 0)
240
+ foreach (var firstTerm in firstTerms)
241
{
103
- // First, clone the project reference so there are enough duplicates for all of the
104
- // requested target frameworks.
105
- for (var i = 1; i < targetFrameworksToSet.Count; ++i)
242
+ var pairSplit = firstTerm.Split(MetadataPairSplitter, 2);
243
+
244
+ // No pair indicator so expand the first term by the second term.
245
+ if (pairSplit.Length == 1)
246
{
107
- additionalProjectReferences.Add(new TaskItem(projectReference));
247
+ if (secondTerms.Count == 0)
248
+ {
249
+ yield return new ExpansionTerms(firstTerm, null);
250
+ }
251
+ else
252
+ {
253
+ foreach (var secondTerm in secondTerms)
254
+ {
255
+ yield return new ExpansionTerms(firstTerm, secondTerm);
256
+ }
257
+ }
258
}
109
-
110
- // Then set the target framework on each project reference.
111
- for (var i = 0; i < targetFrameworksToSet.Count; ++i)
259
+ else // there was a pair like "first|second" or "first|" or "|second" in the first term, so return that value as the pair.
260
{
113
- var reference = (i == 0) ? projectReference : additionalProjectReferences[i - 1];
114
-
115
- this.SetTargetFrameworkOnProjectReference(reference, targetFrameworksToSet[i]);
261
+ yield return new ExpansionTerms(pairSplit[0], pairSplit[1]);
262
}
263
+ }
264
+ }
265
118
- return true;
266
+ private class ProjectReferenceFacade
267
+ {
268
+ private string configuration;
269
+ private string platform;
270
+ private string targetFramework;
271
+ private string runtimeIdentifier;
272
+
273
+ public ProjectReferenceFacade(ITaskItem projectReference, IReadOnlyCollection<string> availableConfigurations, string configuration, IReadOnlyCollection<string> availablePlatforms, string platform, IReadOnlyCollection<string> availableTargetFrameworks, string targetFramework, IReadOnlyCollection<string> availableRuntimeIdentifiers, string runtimeIdentifier, string publishBaseDir)
274
+ {
275
+ this.ProjectReference = projectReference;
276
+ this.AvailableConfigurations = availableConfigurations;
277
+ this.configuration = configuration;
278
+ this.AvailablePlatforms = availablePlatforms;
279
+ this.platform = platform;
280
+ this.AvailableTargetFrameworks = availableTargetFrameworks;
281
+ this.targetFramework = targetFramework;
282
+ this.AvailableRuntimeIdentifiers = availableRuntimeIdentifiers;
283
+ this.runtimeIdentifier = runtimeIdentifier;
284
+ this.PublishBaseDir = publishBaseDir;
285
+ this.Modified = !String.IsNullOrWhiteSpace(configuration) || !String.IsNullOrWhiteSpace(platform) ||
286
+ !String.IsNullOrWhiteSpace(targetFramework) || !String.IsNullOrWhiteSpace(runtimeIdentifier) ||
287
+ !String.IsNullOrWhiteSpace(publishBaseDir);
288
}
289
121
- if (!String.IsNullOrWhiteSpace(setTargetFramework) && !String.IsNullOrWhiteSpace(targetFrameworks))
290
+ public ITaskItem ProjectReference { get; }
291
+
292
+ public bool Modified { get; private set; }
293
+
294
+ public IReadOnlyCollection<string> AvailableConfigurations { get; }
295
+
296
+ public IReadOnlyCollection<string> AvailablePlatforms { get; }
297
+
298
+ public IReadOnlyCollection<string> AvailableRuntimeIdentifiers { get; }
299
+
300
+ public IReadOnlyCollection<string> AvailableTargetFrameworks { get; }
301
+
302
+ public bool Publish => !String.IsNullOrEmpty(this.PublishBaseDir);
303
+
304
+ public string PublishBaseDir { get; }
305
+
306
+ public string Configuration
307
{
123
- this.Log.LogWarning("ProjectReference {0} contains metadata for both SetTargetFramework and TargetFrameworks. SetTargetFramework takes precedent so the TargetFrameworks value '{1}' is ignored", projectReference.ItemSpec, targetFrameworks);
308
+ get => this.configuration;
309
+ set => this.configuration = this.SetWithModified(value, this.configuration);
310
}
311
126
- return false;
127
- }
312
+ public string Platform
313
+ {
314
+ get => this.platform;
315
+ set => this.platform = this.SetWithModified(value, this.platform);
316
+ }
317
129
- private void AddPublishPropertiesToProjectReference(ITaskItem projectReference, string publishDir)
130
- {
131
- var additionalProperties = projectReference.GetMetadata("AdditionalProperties");
132
- if (!String.IsNullOrWhiteSpace(additionalProperties))
318
+ public string TargetFramework
319
{
134
- additionalProperties += ";";
320
+ get => this.targetFramework;
321
+ set => this.targetFramework = this.SetWithModified(value, this.targetFramework);
322
}
323
137
- additionalProperties += "PublishDir=" + publishDir;
324
+ public string RuntimeIdentifier
325
+ {
326
+ get => this.runtimeIdentifier;
327
+ set => this.runtimeIdentifier = this.SetWithModified(value, this.runtimeIdentifier);
328
+ }
329
139
- var bindPath = ToolsCommon.GetMetadataOrDefault(projectReference, "BindPath", publishDir);
330
+ public ProjectReferenceFacade Clone()
331
+ {
332
+ return new ProjectReferenceFacade(new TaskItem(this.ProjectReference), this.AvailableConfigurations, this.configuration, this.AvailablePlatforms, this.platform, this.AvailableTargetFrameworks, this.targetFramework, this.AvailableRuntimeIdentifiers, this.runtimeIdentifier, this.PublishBaseDir);
333
+ }
334
141
- var publishTargets = projectReference.GetMetadata("PublishTargets");
142
- if (String.IsNullOrWhiteSpace(publishTargets))
335
+ public static ProjectReferenceFacade CreateFacade(ITaskItem projectReference, TaskLoggingHelper logger, string intermediateFolder)
336
{
144
- publishTargets = "Publish;GetTargetPath";
337
+ var configurationsValue = new MetadataValueList(projectReference, "Configurations");
338
+ var setConfigurationValue = new MetadataValue(projectReference, "SetConfiguration", "Configuration=");
339
+ var platformsValue = new MetadataValueList(projectReference, "Platforms");
340
+ var setPlatformValue = new MetadataValue(projectReference, "SetPlatform", "Platform=");
341
+ var targetFrameworksValue = new MetadataValueList(projectReference, "TargetFrameworks");
342
+ var setTargetFrameworkValue = new MetadataValue(projectReference, "SetTargetFramework", "TargetFramework=");
343
+ var runtimeIdentifiersValue = new MetadataValueList(projectReference, "RuntimeIdentifiers");
344
+ var publishValue = new MetadataValue(projectReference, "Publish", null);
345
+ var publishDirValue = new MetadataValue(projectReference, "PublishDir", null);
346
+
347
+ var configurations = GetFromListAndValidateSetValue(configurationsValue, setConfigurationValue, logger, projectReference, "Configuration=Release");
348
+
349
+ var platforms = GetFromListAndValidateSetValue(platformsValue, setPlatformValue, logger, projectReference, "Platform=x64");
350
+
351
+ var targetFrameworks = GetFromListAndValidateSetValue(targetFrameworksValue, setTargetFrameworkValue, logger, projectReference, "TargetFramework=tfm");
352
+
353
+ string publishBaseDir = null;
354
+
355
+ if (publishValue.Value.Equals("true", StringComparison.OrdinalIgnoreCase) || (!publishValue.HadValue && publishDirValue.HadValue))
356
+ {
357
+ if (publishDirValue.HadValue)
358
+ {
359
+ publishBaseDir = publishDirValue.Value;
360
+ }
361
+ else
362
+ {
363
+ publishBaseDir = Path.Combine(intermediateFolder, "publish", Path.GetFileNameWithoutExtension(projectReference.ItemSpec));
364
+ }
365
+ }
366
+
367
+ return new ProjectReferenceFacade(projectReference, configurations, null, platforms, null, targetFrameworks, null, runtimeIdentifiersValue.Values, null, publishBaseDir);
368
}
146
- else if (!publishTargets.EndsWith(";GetTargetsPath", StringComparison.OrdinalIgnoreCase))
369
+
370
+ public string CalculatePublishDir()
371
{
148
- publishTargets += ";GetTargetsPath";
372
+ if (!this.Publish)
373
+ {
374
+ return null;
375
+ }
376
+
377
+ var publishDir = this.PublishBaseDir;
378
+
379
+ if (!String.IsNullOrWhiteSpace(this.Configuration))
380
+ {
381
+ publishDir = Path.Combine(publishDir, this.Configuration);
382
+ }
383
+
384
+ if (!String.IsNullOrWhiteSpace(this.Platform))
385
+ {
386
+ publishDir = Path.Combine(publishDir, this.Platform);
387
+ }
388
+
389
+ if (!String.IsNullOrWhiteSpace(this.TargetFramework))
390
+ {
391
+ publishDir = Path.Combine(publishDir, this.TargetFramework);
392
+ }
393
+
394
+ if (!String.IsNullOrWhiteSpace(this.RuntimeIdentifier))
395
+ {
396
+ publishDir = Path.Combine(publishDir, this.RuntimeIdentifier);
397
+ }
398
+
399
+
400
+ return Path.GetFullPath(publishDir);
401
}
402
151
- projectReference.SetMetadata("AdditionalProperties", additionalProperties);
152
- projectReference.SetMetadata("BindPath", bindPath);
153
- projectReference.SetMetadata("Targets", publishTargets);
403
+ public string CalculateBindNameSuffix()
404
+ {
405
+ var sb = new StringBuilder();
406
155
- this.Log.LogMessage(MessageImportance.Low, "Adding publish metadata to project reference {0} Targets {1}, BindPath {2}, AdditionalProperties: {3}",
156
- projectReference.ItemSpec, projectReference.GetMetadata("Targets"), projectReference.GetMetadata("BindPath"), projectReference.GetMetadata("AdditionalProperties"));
157
- }
407
+ if (!String.IsNullOrWhiteSpace(this.Configuration))
408
+ {
409
+ sb.AppendFormat(".{0}", this.Configuration);
410
+ }
411
159
- private void SetTargetFrameworkOnProjectReference(ITaskItem projectReference, string targetFramework)
160
- {
161
- projectReference.SetMetadata("SetTargetFramework", $"TargetFramework={targetFramework}");
412
+ if (!String.IsNullOrWhiteSpace(this.Platform))
413
+ {
414
+ sb.AppendFormat(".{0}", this.Platform);
415
+ }
416
+
417
+ if (!String.IsNullOrWhiteSpace(this.TargetFramework))
418
+ {
419
+ sb.AppendFormat(".{0}", this.TargetFramework);
420
+ }
421
+
422
+ if (!String.IsNullOrWhiteSpace(this.RuntimeIdentifier))
423
+ {
424
+ sb.AppendFormat(".{0}", this.RuntimeIdentifier);
425
+ }
426
+
427
+ return sb.ToString();
428
+ }
429
163
- var bindName = projectReference.GetMetadata("BindName");
164
- if (String.IsNullOrWhiteSpace(bindName))
430
+ private string SetWithModified(string newValue, string oldValue)
431
{
166
- bindName = Path.GetFileNameWithoutExtension(projectReference.ItemSpec);
432
+ if (String.IsNullOrWhiteSpace(newValue) && String.IsNullOrWhiteSpace(oldValue))
433
+ {
434
+ return String.Empty;
435
+ }
436
+ else if (oldValue != newValue)
437
+ {
438
+ this.Modified = true;
439
+ return newValue;
440
+ }
441
168
- projectReference.SetMetadata("BindName", $"{bindName}.{targetFramework}");
442
+ return oldValue;
443
}
444
171
- this.Log.LogMessage(MessageImportance.Low, "Adding target framework metadata to project reference {0} SetTargetFramework: {1}, BindName: {2}",
172
- projectReference.ItemSpec, projectReference.GetMetadata("SetTargetFramework"), projectReference.GetMetadata("BindName"));
173
- }
445
+ private static List<string> GetFromListAndValidateSetValue(MetadataValueList listValue, MetadataValue setValue, TaskLoggingHelper logger, ITaskItem projectReference, string setExample)
446
+ {
447
+ var targetFrameworks = listValue.Values;
448
175
- private static string CalculatePublishDirFromProjectReference(ITaskItem projectReference, string intermediateFolder)
176
- {
177
- var publishDir = Path.Combine("publish", Path.GetFileNameWithoutExtension(projectReference.ItemSpec));
449
+ if (setValue.HadValue)
450
+ {
451
+ if (listValue.HadValue)
452
+ {
453
+ logger.LogMessage("ProjectReference {0} contains metadata for both {1} and {2}. {2} takes precedent so the {1} value '{3}' will be ignored", projectReference.ItemSpec, setValue.Name, listValue.Name, setValue.OriginalValue);
454
+ }
455
+ else if (!setValue.ValidValue)
456
+ {
457
+ logger.LogError("ProjectReference {0} contains invalid {1} value '{2}'. The {1} value should look something like '{3}'.", projectReference.ItemSpec, setValue.Name, setValue.OriginalValue, setExample);
458
+ }
459
+ }
460
179
- return Path.Combine(intermediateFolder, publishDir);
461
+ return targetFrameworks;
462
+ }
463
}
464
182
- private static string AppendTargetFrameworkFromProjectReference(ITaskItem projectReference, string publishDir)
465
+ private class ExpansionTerms
466
{
184
- var setTargetFramework = projectReference.GetMetadata("SetTargetFramework");
185
-
186
- if (setTargetFramework.StartsWith("TargetFramework=") && setTargetFramework.Length > "TargetFramework=".Length)
467
+ public ExpansionTerms(string firstTerm, string secondTerm)
468
{
188
- var targetFramework = setTargetFramework.Substring("TargetFramework=".Length);
189
-
190
- publishDir = Path.Combine(publishDir, targetFramework);
469
+ this.FirstTerm = firstTerm;
470
+ this.SecondTerm = secondTerm;
471
}
472
193
- return publishDir;
473
+ public string FirstTerm { get; }
474
+
475
+ public string SecondTerm { get; }
476
}
477
}
478
}
src/wix/test/WixToolsetTest.Sdk/MsbuildFixture.cs
+131
-1
@@ -670,6 +670,114 @@ namespace WixToolsetTest.Sdk
670
}
671
}
672
673
+ [Theory]
674
+ [InlineData(BuildSystem.DotNetCoreSdk)]
675
+ [InlineData(BuildSystem.MSBuild)]
676
+ [InlineData(BuildSystem.MSBuild64)]
677
+ public void CanBuildMultiTargetingWixlibUsingRids(BuildSystem buildSystem)
678
+ {
679
+ var sourceFolder = TestData.Get(@"TestData", "MultiTargetingWixlib");
680
+
681
+ using (var fs = new TestDataFolderFileSystem())
682
+ {
683
+ fs.Initialize(sourceFolder);
684
+ var baseFolder = Path.Combine(fs.BaseFolder, "PackageUsingRids");
685
+ var binFolder = Path.Combine(baseFolder, @"bin\");
686
+ var filesFolder = Path.Combine(binFolder, "Release", @"PFiles\");
687
+ var projectPath = Path.Combine(baseFolder, "PackageUsingRids.wixproj");
688
+
689
+ var result = MsbuildUtilities.BuildProject(buildSystem, projectPath, new[] {
690
+ "-Restore",
691
+ MsbuildUtilities.GetQuotedPropertySwitch(buildSystem, "WixMSBuildProps", MsbuildFixture.WixPropsPath)
692
+ });
693
+ result.AssertSuccess();
694
+
695
+ var warnings = result.Output.Where(line => line.Contains(": warning")).ToArray();
696
+ WixAssert.StringCollectionEmpty(warnings);
697
+
698
+ var releaseFiles = Directory.EnumerateFiles(filesFolder, "*", SearchOption.AllDirectories);
699
+ var releaseFileSizes = releaseFiles.Select(p => PathAndSize(p, filesFolder)).OrderBy(s => s).ToArray();
700
+
701
+ WixAssert.CompareLineByLine(new[]
702
+ {
703
+ @"net472_x64\e_sqlite3.dll - 1601536",
704
+ @"net472_x86\e_sqlite3.dll - 1207296",
705
+ @"net6_x64\e_sqlite3.dll - 1601536",
706
+ @"net6_x86\e_sqlite3.dll - 1207296",
707
+ }, releaseFileSizes);
708
+ }
709
+ }
710
+
711
+ [Theory]
712
+ [InlineData(BuildSystem.DotNetCoreSdk)]
713
+ [InlineData(BuildSystem.MSBuild)]
714
+ [InlineData(BuildSystem.MSBuild64)]
715
+ public void CanBuildMultiTargetingWixlibUsingRidsWithReleaseAndDebug(BuildSystem buildSystem)
716
+ {
717
+ var sourceFolder = TestData.Get(@"TestData", "MultiTargetingWixlib");
718
+
719
+ using (var fs = new TestDataFolderFileSystem())
720
+ {
721
+ fs.Initialize(sourceFolder);
722
+ var baseFolder = Path.Combine(fs.BaseFolder, "PackageReleaseAndDebug");
723
+ var binFolder = Path.Combine(baseFolder, @"bin\");
724
+ var filesFolder = Path.Combine(binFolder, "Release", @"PFiles\");
725
+ var projectPath = Path.Combine(baseFolder, "PackageReleaseAndDebug.wixproj");
726
+
727
+ var result = MsbuildUtilities.BuildProject(buildSystem, projectPath, new[] {
728
+ "-Restore",
729
+ MsbuildUtilities.GetQuotedPropertySwitch(buildSystem, "WixMSBuildProps", MsbuildFixture.WixPropsPath)
730
+ });
731
+ result.AssertSuccess();
732
+
733
+ var warnings = result.Output.Where(line => line.Contains(": warning")).ToArray();
734
+ WixAssert.StringCollectionEmpty(warnings);
735
+
736
+ var releaseFiles = Directory.EnumerateFiles(filesFolder, "*", SearchOption.AllDirectories);
737
+ var releaseFileSizes = releaseFiles.Select(p => PathAndSize(p, filesFolder)).OrderBy(s => s).ToArray();
738
+
739
+ WixAssert.CompareLineByLine(new[]
740
+ {
741
+ @"debug_net472_x64\e_sqlite3.dll - 1601536",
742
+ @"debug_net472_x86\e_sqlite3.dll - 1207296",
743
+ @"debug_net6_x64\e_sqlite3.dll - 1601536",
744
+ @"debug_net6_x86\e_sqlite3.dll - 1207296",
745
+ @"release_net472_x64\e_sqlite3.dll - 1601536",
746
+ @"release_net472_x86\e_sqlite3.dll - 1207296",
747
+ @"release_net6_x64\e_sqlite3.dll - 1601536",
748
+ @"release_net6_x86\e_sqlite3.dll - 1207296",
749
+ }, releaseFileSizes);
750
+ }
751
+ }
752
+
753
+ [Theory]
754
+ [InlineData(BuildSystem.DotNetCoreSdk)]
755
+ [InlineData(BuildSystem.MSBuild)]
756
+ [InlineData(BuildSystem.MSBuild64)]
757
+ public void CannotBuildMultiTargetingWixlibUsingExplicitSubsetOfTfmAndRid(BuildSystem buildSystem)
758
+ {
759
+ var sourceFolder = TestData.Get(@"TestData", "MultiTargetingWixlib");
760
+
761
+ using (var fs = new TestDataFolderFileSystem())
762
+ {
763
+ fs.Initialize(sourceFolder);
764
+ var baseFolder = Path.Combine(fs.BaseFolder, "PackageUsingExplicitTfmAndRids");
765
+ var binFolder = Path.Combine(baseFolder, @"bin\");
766
+ var filesFolder = Path.Combine(binFolder, "Release", @"PFiles\");
767
+ var projectPath = Path.Combine(baseFolder, "PackageUsingExplicitTfmAndRids.wixproj");
768
+
769
+ var result = MsbuildUtilities.BuildProject(buildSystem, projectPath, new[] {
770
+ "-Restore",
771
+ MsbuildUtilities.GetQuotedPropertySwitch(buildSystem, "WixMSBuildProps", MsbuildFixture.WixPropsPath)
772
+ });
773
+
774
+ var errors = GetDistinctErrorMessages(result.Output, baseFolder);
775
+ WixAssert.CompareLineByLine(new[]
776
+ {
777
+ @"<basefolder>\Package.wxs(22): error WIX0103: Cannot find the File file '!(bindpath.TestExe.net472.win_x86)\e_sqlite3.dll'. The following paths were checked: !(bindpath.TestExe.net472.win_x86)\e_sqlite3.dll [<basefolder>\PackageUsingExplicitTfmAndRids.wixproj]",
778
+ }, errors);
779
+ }
780
+ }
781
782
[Theory]
783
[InlineData(BuildSystem.DotNetCoreSdk)]
@@ -773,9 +881,31 @@ namespace WixToolsetTest.Sdk
881
return ReplacePathsInMessage(message.Substring(start, end - start), baseFolder);
882
}
883
884
+ private static string[] GetDistinctErrorMessages(string[] output, string baseFolder)
885
+ {
886
+ return output.Where(l => l.Contains(": error ")).Select(line =>
887
+ {
888
+ var trimmed = ReplacePathsInMessage(line, baseFolder);
889
+
890
+ // If the message starts with a multi-proc build marker (like: "1>" or "2>") trim it.
891
+ if (trimmed[1] == '>')
892
+ {
893
+ trimmed = trimmed.Substring(2);
894
+ }
895
+
896
+ return trimmed;
897
+ }).Distinct().ToArray();
898
+ }
899
+
900
private static string ReplacePathsInMessage(string message, string baseFolder)
901
{
778
- return message.Replace(baseFolder, "<basefolder>").Trim();
902
+ return message.Trim().Replace(baseFolder, "<basefolder>");
903
+ }
904
+
905
+ private static string PathAndSize(string path, string replace)
906
+ {
907
+ var fi = new FileInfo(path);
908
+ return $"{fi.FullName.Replace(replace, String.Empty)} - {fi.Length}";
909
}
910
}
911
}
src/wix/test/WixToolsetTest.Sdk/TestData/MultiTargetingWixlib/Directory.Build.props
new
+3
@@ -0,0 +1,3 @@
1
+<Project>
2
+ <!-- Reset any Directory.Build.props that might exists above this folder -->
3
+</Project>
\ No newline at end of file
src/wix/test/WixToolsetTest.Sdk/TestData/MultiTargetingWixlib/Directory.Build.targets
new
+3
@@ -0,0 +1,3 @@
1
+<Project>
2
+ <!-- Reset any Directory.Build.targets that might exists above this folder -->
3
+</Project>
\ No newline at end of file
src/wix/test/WixToolsetTest.Sdk/TestData/MultiTargetingWixlib/PackageReleaseAndDebug/Package.wxs
new
+36
@@ -0,0 +1,36 @@
1
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2
+ <Package Name="Test Package" Manufacturer="~Test" Version="0" Compressed="false" UpgradeCode="11111111-1111-1111-1111-111111111111">
3
+ <Feature Id="Main">
4
+ <ComponentGroupRef Id="Stuff"/>
5
+ </Feature>
6
+ </Package>
7
+
8
+ <Fragment>
9
+ <ComponentGroup Id="Stuff" Directory="ProgramFilesFolder">
10
+ <Component Subdirectory="debug_net472_x86">
11
+ <File Id="debug_net472_x86" Source="!(bindpath.TestExe.Debug.net472.win_x86)\e_sqlite3.dll" />
12
+ </Component>
13
+ <Component Subdirectory="debug_net472_x64">
14
+ <File Id="debug_net472_x64" Source="!(bindpath.TestExe.Debug.net472.win_x64)\e_sqlite3.dll" />
15
+ </Component>
16
+ <Component Subdirectory="debug_net6_x86">
17
+ <File Id="debug_net6_x86" Source="!(bindpath.TestExe.Debug.net6.0.win_x86)\e_sqlite3.dll" />
18
+ </Component>
19
+ <Component Subdirectory="debug_net6_x64">
20
+ <File Id="debug_net6_x64" Source="!(bindpath.TestExe.Debug.net6.0.win_x64)\e_sqlite3.dll" />
21
+ </Component>
22
+ <Component Subdirectory="release_net472_x86">
23
+ <File Id="release_net472_x86" Source="!(bindpath.TestExe.Release.net472.win_x86)\e_sqlite3.dll" />
24
+ </Component>
25
+ <Component Subdirectory="release_net472_x64">
26
+ <File Id="release_net472_x64" Source="!(bindpath.TestExe.Release.net472.win_x64)\e_sqlite3.dll" />
27
+ </Component>
28
+ <Component Subdirectory="release_net6_x86">
29
+ <File Id="release_net6_x86" Source="!(bindpath.TestExe.Release.net6.0.win_x86)\e_sqlite3.dll" />
30
+ </Component>
31
+ <Component Subdirectory="release_net6_x64">
32
+ <File Id="release_net6_x64" Source="!(bindpath.TestExe.Release.net6.0.win_x64)\e_sqlite3.dll" />
33
+ </Component>
34
+ </ComponentGroup>
35
+ </Fragment>
36
+</Wix>
src/wix/test/WixToolsetTest.Sdk/TestData/MultiTargetingWixlib/PackageReleaseAndDebug/PackageReleaseAndDebug.wixproj
new
+10
@@ -0,0 +1,10 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<Project>
3
+ <Import Project="$(WixMSBuildProps)" />
4
+
5
+ <ItemGroup>
6
+ <ProjectReference Include="..\TestExe\TestExe.csproj" Publish="true" Configurations="Debug,Release" TargetFrameworks="net6.0,net472" RuntimeIdentifiers="win-x86,win-x64" />
7
+ </ItemGroup>
8
+
9
+ <Import Project="$(WixTargetsPath)" />
10
+</Project>
src/wix/test/WixToolsetTest.Sdk/TestData/MultiTargetingWixlib/PackageUsingExplicitTfmAndRids/Package.wxs
new
+26
@@ -0,0 +1,26 @@
1
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2
+ <Package Name="Test Package" Manufacturer="~Test" Version="0" Compressed="false" UpgradeCode="11111111-1111-1111-1111-111111111111">
3
+ <Feature Id="Main">
4
+ <ComponentGroupRef Id="Stuff"/>
5
+ </Feature>
6
+ </Package>
7
+
8
+ <Fragment>
9
+ <ComponentGroup Id="Stuff" Directory="ProgramFilesFolder">
10
+ <Component Subdirectory="net472_x64">
11
+ <File Id="net472_x64" Source="!(bindpath.TestExe.net472.win_x64)\e_sqlite3.dll" />
12
+ </Component>
13
+ <Component Subdirectory="net6_x86">
14
+ <File Id="net6_x86" Source="!(bindpath.TestExe.net6.0.win_x86)\e_sqlite3.dll" />
15
+ </Component>
16
+ <Component Subdirectory="net6_x64">
17
+ <File Id="net6_x64" Source="!(bindpath.TestExe.net6.0.win_x64)\e_sqlite3.dll" />
18
+ </Component>
19
+
20
+ <!-- This is explicitly not built and causes the build to fail -->
21
+ <Component Subdirectory="net472_x86">
22
+ <File Id="net472_x86" Source="!(bindpath.TestExe.net472.win_x86)\e_sqlite3.dll" />
23
+ </Component>
24
+ </ComponentGroup>
25
+ </Fragment>
26
+</Wix>
src/wix/test/WixToolsetTest.Sdk/TestData/MultiTargetingWixlib/PackageUsingExplicitTfmAndRids/PackageUsingExplicitTfmAndRids.wixproj
new
+10
@@ -0,0 +1,10 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<Project>
3
+ <Import Project="$(WixMSBuildProps)" />
4
+
5
+ <ItemGroup>
6
+ <ProjectReference Include="..\TestExe\TestExe.csproj" Publish="true" TargetFrameworks="net6.0,net472|win-x64" RuntimeIdentifiers="win-x86,win-x64" />
7
+ </ItemGroup>
8
+
9
+ <Import Project="$(WixTargetsPath)" />
10
+</Project>
src/wix/test/WixToolsetTest.Sdk/TestData/MultiTargetingWixlib/PackageUsingRids/Package.wxs
new
+24
@@ -0,0 +1,24 @@
1
+<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2
+ <Package Name="Test Package" Manufacturer="~Test" Version="0" Compressed="false" UpgradeCode="11111111-1111-1111-1111-111111111111">
3
+ <Feature Id="Main">
4
+ <ComponentGroupRef Id="Stuff"/>
5
+ </Feature>
6
+ </Package>
7
+
8
+ <Fragment>
9
+ <ComponentGroup Id="Stuff" Directory="ProgramFilesFolder">
10
+ <Component Subdirectory="net472_x86">
11
+ <File Id="net472_x86" Source="!(bindpath.TestExe.net472.win_x86)\e_sqlite3.dll" />
12
+ </Component>
13
+ <Component Subdirectory="net472_x64">
14
+ <File Id="net472_x64" Source="!(bindpath.TestExe.net472.win_x64)\e_sqlite3.dll" />
15
+ </Component>
16
+ <Component Subdirectory="net6_x86">
17
+ <File Id="net6_x86" Source="!(bindpath.TestExe.net6.0.win_x86)\e_sqlite3.dll" />
18
+ </Component>
19
+ <Component Subdirectory="net6_x64">
20
+ <File Id="net6_x64" Source="!(bindpath.TestExe.net6.0.win_x64)\e_sqlite3.dll" />
21
+ </Component>
22
+ </ComponentGroup>
23
+ </Fragment>
24
+</Wix>
src/wix/test/WixToolsetTest.Sdk/TestData/MultiTargetingWixlib/PackageUsingRids/PackageUsingRids.wixproj
new
+10
@@ -0,0 +1,10 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<Project>
3
+ <Import Project="$(WixMSBuildProps)" />
4
+
5
+ <ItemGroup>
6
+ <ProjectReference Include="..\TestExe\TestExe.csproj" Publish="true" TargetFrameworks="net6.0,net472" RuntimeIdentifiers="win-x86,win-x64" />
7
+ </ItemGroup>
8
+
9
+ <Import Project="$(WixTargetsPath)" />
10
+</Project>
src/wix/test/WixToolsetTest.Sdk/TestData/MultiTargetingWixlib/TestExe/Program.cs
new
+14
@@ -0,0 +1,14 @@
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 WixToolsetTest.Sdk.TestData.MultiTargetingWixlib.MultiTargetingClassLib
4
+{
5
+ using System;
6
+
7
+ public class Program
8
+ {
9
+ public static void Main(string[] args)
10
+ {
11
+ Console.WriteLine("This is Test.exe");
12
+ }
13
+ }
14
+}
src/wix/test/WixToolsetTest.Sdk/TestData/MultiTargetingWixlib/TestExe/TestExe.csproj
new
+12
@@ -0,0 +1,12 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<Project Sdk="Microsoft.NET.Sdk">
3
+ <PropertyGroup>
4
+ <OutputType>Exe</OutputType>
5
+ <TargetFrameworks>net6.0;net472</TargetFrameworks>
6
+ <RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
7
+ </PropertyGroup>
8
+
9
+ <ItemGroup>
10
+ <PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.0" />
11
+ </ItemGroup>
12
+</Project>