@joebigelow / wix / commits / 69f11ee0

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>