summaryrefslogtreecommitdiff
path: root/src/tools/heat/VSProjectHarvester.cs
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-07-26 17:20:39 -0700
committerRob Mensching <rob@firegiant.com>2022-08-01 20:25:19 -0700
commita627ca9b720047e633a8fe72003ab9bee31006c5 (patch)
tree2bc8a924bb4141ab718e74d08f6459a0ffe8d573 /src/tools/heat/VSProjectHarvester.cs
parent521eb3c9cf38823a2c4019abb85dc0b3200b92cb (diff)
downloadwix-a627ca9b720047e633a8fe72003ab9bee31006c5.tar.gz
wix-a627ca9b720047e633a8fe72003ab9bee31006c5.tar.bz2
wix-a627ca9b720047e633a8fe72003ab9bee31006c5.zip
Create WixToolset.Heat.nupkg to distribute heat.exe and Heat targets
Moves Heat functionality to the "tools" layer and packages it all up in WixToolset.Heat.nupkg for distribution in WiX v4. Completes 6838
Diffstat (limited to 'src/tools/heat/VSProjectHarvester.cs')
-rw-r--r--src/tools/heat/VSProjectHarvester.cs1455
1 files changed, 1455 insertions, 0 deletions
diff --git a/src/tools/heat/VSProjectHarvester.cs b/src/tools/heat/VSProjectHarvester.cs
new file mode 100644
index 00000000..93b20cd8
--- /dev/null
+++ b/src/tools/heat/VSProjectHarvester.cs
@@ -0,0 +1,1455 @@
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
3namespace WixToolset.Harvesters
4{
5 using System;
6 using System.IO;
7 using System.Reflection;
8 using System.Collections;
9 using System.Collections.Generic;
10 using System.Globalization;
11 using System.Text.RegularExpressions;
12 using System.Xml;
13 using WixToolset.Data;
14 using WixToolset.Extensibility.Services;
15 using WixToolset.Harvesters.Data;
16 using WixToolset.Harvesters.Extensibility;
17 using Wix = WixToolset.Harvesters.Serialize;
18
19 /// <summary>
20 /// Harvest WiX authoring for the outputs of a VS project.
21 /// </summary>
22 public sealed class VSProjectHarvester : BaseHarvesterExtension
23 {
24 // These format strings are used for generated element identifiers.
25 // {0} = project name
26 // {1} = POG name
27 // {2} = file name
28 private const string DirectoryIdFormat = "{0}.{1}";
29 private const string ComponentIdFormat = "{0}.{1}.{2}";
30 private const string FileIdFormat = "{0}.{1}.{2}";
31 private const string VariableFormat = "$(var.{0}.{1})";
32 private const string WixVariableFormat = "!(wix.{0}.{1})";
33
34 private const string ComponentPrefix = "cmp";
35 private const string DirectoryPrefix = "dir";
36 private const string FilePrefix = "fil";
37
38 private string projectGUID;
39 private string directoryIds;
40 private string directoryRefSeed;
41 private string projectName;
42 private string configuration;
43 private string platform;
44 private bool setUniqueIdentifiers;
45 private GenerateType generateType;
46 private bool generateWixVars;
47
48
49 private static readonly ProjectOutputGroup[] allOutputGroups = new ProjectOutputGroup[]
50 {
51 new ProjectOutputGroup("Binaries", "BuiltProjectOutputGroup", "TargetDir"),
52 new ProjectOutputGroup("Symbols", "DebugSymbolsProjectOutputGroup", "TargetDir"),
53 new ProjectOutputGroup("Documents", "DocumentationProjectOutputGroup", "ProjectDir"),
54 new ProjectOutputGroup("Satellites", "SatelliteDllsProjectOutputGroup", "TargetDir"),
55 new ProjectOutputGroup("Sources", "SourceFilesProjectOutputGroup", "ProjectDir"),
56 new ProjectOutputGroup("Content", "ContentFilesProjectOutputGroup", "ProjectDir"),
57 };
58
59 private string[] outputGroups;
60
61 /// <summary>
62 /// Instantiate a new VSProjectHarvester.
63 /// </summary>
64 /// <param name="outputGroups">List of project output groups to harvest.</param>
65 public VSProjectHarvester(string[] outputGroups)
66 {
67 if (outputGroups == null)
68 {
69 throw new ArgumentNullException("outputGroups");
70 }
71
72 this.outputGroups = outputGroups;
73 }
74
75 /// <summary>
76 /// Gets or sets the configuration to set when harvesting.
77 /// </summary>
78 /// <value>The configuration to set when harvesting.</value>
79 public string Configuration
80 {
81 get { return this.configuration; }
82 set { this.configuration = value; }
83 }
84
85 public string DirectoryIds
86 {
87 get { return this.directoryIds; }
88 set { this.directoryIds = value; }
89 }
90
91 /// <summary>
92 /// Gets or sets what type of elements are to be generated.
93 /// </summary>
94 /// <value>The type of elements being generated.</value>
95 public GenerateType GenerateType
96 {
97 get { return this.generateType; }
98 set { this.generateType = value; }
99 }
100
101 /// <summary>
102 /// Gets or sets whether or not to use wix variables.
103 /// </summary>
104 /// <value>Whether or not to use wix variables.</value>
105 public bool GenerateWixVars
106 {
107 get { return this.generateWixVars; }
108 set { this.generateWixVars = value; }
109 }
110
111 /// <summary>
112 /// Gets or sets the location to load MSBuild from.
113 /// </summary>
114 public string MsbuildBinPath { get; set; }
115
116 /// <summary>
117 /// Gets or sets the platform to set when harvesting.
118 /// </summary>
119 /// <value>The platform to set when harvesting.</value>
120 public string Platform
121 {
122 get { return this.platform; }
123 set { this.platform = value; }
124 }
125
126 /// <summary>
127 /// Gets or sets the project name to use in wix variables.
128 /// </summary>
129 /// <value>The project name to use in wix variables.</value>
130 public string ProjectName
131 {
132 get { return this.projectName; }
133 set { this.projectName = value; }
134 }
135
136 /// <summary>
137 /// Gets or sets the option to set unique identifiers.
138 /// </summary>
139 /// <value>The option to set unique identifiers.</value>
140 public bool SetUniqueIdentifiers
141 {
142 get { return this.setUniqueIdentifiers; }
143 set { this.setUniqueIdentifiers = value; }
144 }
145
146 /// <summary>
147 /// Gets or sets whether to ignore MsbuildBinPath when the project file specifies a known MSBuild version.
148 /// </summary>
149 public bool UseToolsVersion { get; set; }
150
151 /// <summary>
152 /// Gets a list of friendly output group names that will be recognized on the command-line.
153 /// </summary>
154 /// <returns>Array of output group names.</returns>
155 public static string[] GetOutputGroupNames()
156 {
157 string[] names = new string[VSProjectHarvester.allOutputGroups.Length];
158 for (int i = 0; i < names.Length; i++)
159 {
160 names[i] = VSProjectHarvester.allOutputGroups[i].Name;
161 }
162 return names;
163 }
164
165 /// <summary>
166 /// Harvest a VS project.
167 /// </summary>
168 /// <param name="argument">The path of the VS project file.</param>
169 /// <returns>The harvested directory.</returns>
170 public override Wix.Fragment[] Harvest(string argument)
171 {
172 if (null == argument)
173 {
174 throw new ArgumentNullException("argument");
175 }
176
177 if (!System.IO.File.Exists(argument))
178 {
179 throw new FileNotFoundException(argument);
180 }
181
182 // Match specified output group names to available POG structures
183 // and collect list of build output groups to pass to MSBuild.
184 ProjectOutputGroup[] pogs = new ProjectOutputGroup[this.outputGroups.Length];
185 string[] buildOutputGroups = new string[this.outputGroups.Length];
186 for (int i = 0; i < this.outputGroups.Length; i++)
187 {
188 foreach (ProjectOutputGroup pog in VSProjectHarvester.allOutputGroups)
189 {
190 if (pog.Name == this.outputGroups[i])
191 {
192 pogs[i] = pog;
193 buildOutputGroups[i] = pog.BuildOutputGroup;
194 }
195 }
196
197 if (buildOutputGroups[i] == null)
198 {
199 throw new WixException(HarvesterErrors.InvalidOutputGroup(this.outputGroups[i]));
200 }
201 }
202
203 string projectFile = Path.GetFullPath(argument);
204
205 IDictionary buildOutputs = this.GetProjectBuildOutputs(projectFile, buildOutputGroups);
206
207 ArrayList fragmentList = new ArrayList();
208
209 for (int i = 0; i < pogs.Length; i++)
210 {
211 this.HarvestProjectOutputGroup(projectFile, buildOutputs, pogs[i], fragmentList);
212 }
213
214 return (Wix.Fragment[]) fragmentList.ToArray(typeof(Wix.Fragment));
215 }
216
217 /// <summary>
218 /// Runs MSBuild on a project file to get the list of filenames for the specified output groups.
219 /// </summary>
220 /// <param name="projectFile">VS MSBuild project file to load.</param>
221 /// <param name="buildOutputGroups">List of MSBuild output group names.</param>
222 /// <returns>Dictionary mapping output group names to lists of filenames in the group.</returns>
223 private IDictionary GetProjectBuildOutputs(string projectFile, string[] buildOutputGroups)
224 {
225 MSBuildProject project = this.GetMsbuildProject(projectFile);
226
227 project.Load(projectFile);
228
229 IDictionary buildOutputs = new Hashtable();
230
231 string originalDirectory = System.IO.Directory.GetCurrentDirectory();
232 System.IO.Directory.SetCurrentDirectory(Path.GetDirectoryName(projectFile));
233 bool buildSuccess = false;
234 try
235 {
236 buildSuccess = project.Build(projectFile, buildOutputGroups, buildOutputs);
237 }
238 finally
239 {
240 System.IO.Directory.SetCurrentDirectory(originalDirectory);
241 }
242
243 if (!buildSuccess)
244 {
245 throw new WixException(HarvesterErrors.BuildFailed());
246 }
247
248 this.projectGUID = project.GetEvaluatedProperty("ProjectGuid");
249
250 if (null == this.projectGUID)
251 {
252 throw new WixException(HarvesterErrors.BuildFailed());
253 }
254
255 IDictionary newDictionary = new Dictionary<object, object>();
256 foreach (string buildOutput in buildOutputs.Keys)
257 {
258 IEnumerable buildOutputFiles = buildOutputs[buildOutput] as IEnumerable;
259
260 bool hasFiles = false;
261
262 foreach (object file in buildOutputFiles)
263 {
264 hasFiles = true;
265 break;
266 }
267
268 // Try the item group if no outputs
269 if (!hasFiles)
270 {
271 IEnumerable itemFiles = project.GetEvaluatedItemsByName(String.Concat(buildOutput, "Output"));
272 List<object> itemFileList = new List<object>();
273
274 // Get each BuildItem and add the file path to our list
275 foreach (object itemFile in itemFiles)
276 {
277 itemFileList.Add(project.GetBuildItem(itemFile));
278 }
279
280 // Use our list for this build output
281 newDictionary.Add(buildOutput, itemFileList);
282 }
283 else
284 {
285 newDictionary.Add(buildOutput, buildOutputFiles);
286 }
287 }
288
289 return newDictionary;
290 }
291
292 /// <summary>
293 /// Creates WiX fragments for files in one output group.
294 /// </summary>
295 /// <param name="projectFile">VS MSBuild project file.</param>
296 /// <param name="buildOutputs">Dictionary of build outputs retrieved from an MSBuild run on the project file.</param>
297 /// <param name="pog">Project output group parameters.</param>
298 /// <param name="fragmentList">List to which generated fragments will be added.</param>
299 /// <returns>Count of harvested files.</returns>
300 private int HarvestProjectOutputGroup(string projectFile, IDictionary buildOutputs, ProjectOutputGroup pog, IList fragmentList)
301 {
302 string projectName = Path.GetFileNameWithoutExtension(projectFile);
303 string projectBaseDir = null;
304
305 if (this.ProjectName != null)
306 {
307 projectName = this.ProjectName;
308 }
309
310 string sanitizedProjectName = this.Core.CreateIdentifierFromFilename(projectName);
311
312 Wix.IParentElement harvestParent;
313
314 if (this.GenerateType == GenerateType.Container)
315 {
316 Wix.Container container = new Wix.Container();
317 harvestParent = container;
318
319 container.Name = String.Format(CultureInfo.InvariantCulture, DirectoryIdFormat, sanitizedProjectName, pog.Name);
320 }
321 else if (this.GenerateType == GenerateType.PayloadGroup)
322 {
323 Wix.PayloadGroup payloadGroup = new Wix.PayloadGroup();
324 harvestParent = payloadGroup;
325
326 payloadGroup.Id = String.Format(CultureInfo.InvariantCulture, DirectoryIdFormat, sanitizedProjectName, pog.Name);
327 }
328 else if (this.GenerateType == GenerateType.PackageGroup)
329 {
330 Wix.PackageGroup packageGroup = new Wix.PackageGroup();
331 harvestParent = packageGroup;
332
333 packageGroup.Id = String.Format(CultureInfo.InvariantCulture, DirectoryIdFormat, sanitizedProjectName, pog.Name);
334 }
335 else
336 {
337 Wix.DirectoryRef directoryRef = new Wix.DirectoryRef();
338 harvestParent = directoryRef;
339
340 if (!String.IsNullOrEmpty(this.directoryIds))
341 {
342 directoryRef.Id = this.directoryIds;
343 }
344 else if (this.setUniqueIdentifiers)
345 {
346 directoryRef.Id = String.Format(CultureInfo.InvariantCulture, DirectoryIdFormat, sanitizedProjectName, pog.Name);
347 }
348 else
349 {
350 directoryRef.Id = this.Core.CreateIdentifierFromFilename(String.Format(CultureInfo.InvariantCulture, DirectoryIdFormat, sanitizedProjectName, pog.Name));
351 }
352
353 this.directoryRefSeed = this.Core.GenerateIdentifier(DirectoryPrefix, this.projectGUID, pog.Name);
354 }
355
356 IEnumerable pogFiles = buildOutputs[pog.BuildOutputGroup] as IEnumerable;
357 if (pogFiles == null)
358 {
359 throw new WixException(HarvesterErrors.MissingProjectOutputGroup(
360 projectFile, pog.BuildOutputGroup));
361 }
362
363 if (pog.FileSource == "ProjectDir")
364 {
365 projectBaseDir = Path.GetDirectoryName(projectFile) + "\\";
366 }
367
368 int harvestCount = this.HarvestProjectOutputGroupFiles(projectBaseDir, projectName, pog.Name, pog.FileSource, pogFiles, harvestParent);
369
370 if (this.GenerateType == GenerateType.Container)
371 {
372 // harvestParent must be a Container at this point
373 Wix.Container container = harvestParent as Wix.Container;
374
375 Wix.Fragment fragment = new Wix.Fragment();
376 fragment.AddChild(container);
377 fragmentList.Add(fragment);
378 }
379 else if (this.GenerateType == GenerateType.PackageGroup)
380 {
381 // harvestParent must be a PackageGroup at this point
382 Wix.PackageGroup packageGroup = harvestParent as Wix.PackageGroup;
383
384 Wix.Fragment fragment = new Wix.Fragment();
385 fragment.AddChild(packageGroup);
386 fragmentList.Add(fragment);
387 }
388 else if (this.GenerateType == GenerateType.PayloadGroup)
389 {
390 // harvestParent must be a Container at this point
391 Wix.PayloadGroup payloadGroup = harvestParent as Wix.PayloadGroup;
392
393 Wix.Fragment fragment = new Wix.Fragment();
394 fragment.AddChild(payloadGroup);
395 fragmentList.Add(fragment);
396 }
397 else
398 {
399 // harvestParent must be a DirectoryRef at this point
400 Wix.DirectoryRef directoryRef = harvestParent as Wix.DirectoryRef;
401
402 if (harvestCount > 0)
403 {
404 Wix.Fragment drf = new Wix.Fragment();
405 drf.AddChild(directoryRef);
406 fragmentList.Add(drf);
407 }
408
409 Wix.ComponentGroup cg = new Wix.ComponentGroup();
410
411 if (this.setUniqueIdentifiers || !String.IsNullOrEmpty(this.directoryIds))
412 {
413 cg.Id = String.Format(CultureInfo.InvariantCulture, DirectoryIdFormat, sanitizedProjectName, pog.Name);
414 }
415 else
416 {
417 cg.Id = directoryRef.Id;
418 }
419
420 if (harvestCount > 0)
421 {
422 this.AddComponentsToComponentGroup(directoryRef, cg);
423 }
424
425 Wix.Fragment cgf = new Wix.Fragment();
426 cgf.AddChild(cg);
427 fragmentList.Add(cgf);
428 }
429
430 return harvestCount;
431 }
432
433 /// <summary>
434 /// Add all Components in an element tree to a ComponentGroup.
435 /// </summary>
436 /// <param name="parent">Parent of an element tree that will be searched for Components.</param>
437 /// <param name="cg">The ComponentGroup the Components will be added to.</param>
438 private void AddComponentsToComponentGroup(Wix.IParentElement parent, Wix.ComponentGroup cg)
439 {
440 foreach (Wix.ISchemaElement childElement in parent.Children)
441 {
442 Wix.Component c = childElement as Wix.Component;
443 if (c != null)
444 {
445 Wix.ComponentRef cr = new Wix.ComponentRef();
446 cr.Id = c.Id;
447 cg.AddChild(cr);
448 }
449 else
450 {
451 Wix.IParentElement p = childElement as Wix.IParentElement;
452 if (p != null)
453 {
454 this.AddComponentsToComponentGroup(p, cg);
455 }
456 }
457 }
458 }
459
460 /// <summary>
461 /// Harvest files from one output group of a VS project.
462 /// </summary>
463 /// <param name="baseDir">The base directory of the files.</param>
464 /// <param name="projectName">Name of the project, to be used as a prefix for generated identifiers.</param>
465 /// <param name="pogName">Name of the project output group, used for generating identifiers for WiX elements.</param>
466 /// <param name="pogFileSource">The ProjectOutputGroup file source.</param>
467 /// <param name="outputGroupFiles">The files from one output group to harvest.</param>
468 /// <param name="parent">The parent element that will contain the components of the harvested files.</param>
469 /// <returns>The number of files harvested.</returns>
470 private int HarvestProjectOutputGroupFiles(string baseDir, string projectName, string pogName, string pogFileSource, IEnumerable outputGroupFiles, Wix.IParentElement parent)
471 {
472 int fileCount = 0;
473
474 Wix.ISchemaElement exeFile = null;
475 Wix.ISchemaElement dllFile = null;
476 Wix.ISchemaElement appConfigFile = null;
477
478 // Keep track of files inserted
479 // Files can have different absolute paths but get mapped to the same SourceFile
480 // after the project variables have been used. For example, a WiX project that
481 // is building multiple cultures will have many output MSIs/MSMs, but will all get
482 // mapped to $(var.ProjName.TargetDir)\ProjName.msm. These duplicates would
483 // prevent generated code from compiling.
484 Dictionary<string, bool> seenList = new Dictionary<string,bool>();
485
486 foreach (object output in outputGroupFiles)
487 {
488 string filePath = output.ToString();
489 string fileName = Path.GetFileName(filePath);
490 string fileDir = Path.GetDirectoryName(filePath);
491 string link = null;
492
493 MethodInfo getMetadataMethod = output.GetType().GetMethod("GetMetadata");
494 if (getMetadataMethod != null)
495 {
496 link = (string)getMetadataMethod.Invoke(output, new object[] { "Link" });
497 if (!String.IsNullOrEmpty(link))
498 {
499 fileDir = Path.GetDirectoryName(Path.Combine(baseDir, link));
500 }
501 }
502
503 Wix.IParentElement parentDir = parent;
504 // Ignore Containers and PayloadGroups because they do not have a nested structure.
505 if (baseDir != null && !String.Equals(Path.GetDirectoryName(baseDir), fileDir, StringComparison.OrdinalIgnoreCase)
506 && this.GenerateType != GenerateType.Container && this.GenerateType != GenerateType.PackageGroup && this.GenerateType != GenerateType.PayloadGroup)
507 {
508 Uri baseUri = new Uri(baseDir);
509 Uri relativeUri = baseUri.MakeRelativeUri(new Uri(fileDir));
510 parentDir = this.GetSubDirElement(parentDir, relativeUri);
511 }
512
513 string parentDirId = null;
514
515 if (parentDir is Wix.DirectoryRef)
516 {
517 parentDirId = this.directoryRefSeed;
518 }
519 else if (parentDir is Wix.Directory)
520 {
521 parentDirId = ((Wix.Directory)parentDir).Id;
522 }
523
524 if (this.GenerateType == GenerateType.Container || this.GenerateType == GenerateType.PayloadGroup)
525 {
526 Wix.Payload payload = new Wix.Payload();
527
528 this.HarvestProjectOutputGroupPayloadFile(baseDir, projectName, pogName, pogFileSource, filePath, fileName, link, parentDir, payload, seenList);
529 }
530 else if (this.GenerateType == GenerateType.PackageGroup)
531 {
532 this.HarvestProjectOutputGroupPackage(projectName, pogName, pogFileSource, filePath, fileName, link, parentDir, seenList);
533 }
534 else
535 {
536 Wix.Component component = new Wix.Component();
537 Wix.File file = new Wix.File();
538
539 this.HarvestProjectOutputGroupFile(baseDir, projectName, pogName, pogFileSource, filePath, fileName, link, parentDir, parentDirId, component, file, seenList);
540
541 if (String.Equals(Path.GetExtension(file.Source), ".exe", StringComparison.OrdinalIgnoreCase))
542 {
543 exeFile = file;
544 }
545 else if (String.Equals(Path.GetExtension(file.Source), ".dll", StringComparison.OrdinalIgnoreCase))
546 {
547 dllFile = file;
548 }
549 else if (file.Source.EndsWith("app.config", StringComparison.OrdinalIgnoreCase))
550 {
551 appConfigFile = file;
552 }
553 }
554
555 fileCount++;
556 }
557
558 // if there was no exe file found fallback on the dll file found
559 if (exeFile == null && dllFile != null)
560 {
561 exeFile = dllFile;
562 }
563
564 // Special case for the app.config file in the Binaries POG...
565 // The POG refers to the files in the OBJ directory, while the
566 // generated WiX code references them in the bin directory.
567 // The app.config file gets renamed to match the exe name.
568 if ("Binaries" == pogName && null != exeFile && null != appConfigFile)
569 {
570 if (appConfigFile is Wix.File)
571 {
572 Wix.File appConfigFileAsWixFile = appConfigFile as Wix.File;
573 Wix.File exeFileAsWixFile = exeFile as Wix.File;
574 // Case insensitive replace
575 appConfigFileAsWixFile.Source = Regex.Replace(appConfigFileAsWixFile.Source, @"app\.config", Path.GetFileName(exeFileAsWixFile.Source) + ".config", RegexOptions.IgnoreCase);
576 }
577 }
578
579 return fileCount;
580 }
581
582 private void HarvestProjectOutputGroupFile(string baseDir, string projectName, string pogName, string pogFileSource, string filePath, string fileName, string link, Wix.IParentElement parentDir, string parentDirId, Wix.Component component, Wix.File file, Dictionary<string, bool> seenList)
583 {
584 string varFormat = VariableFormat;
585 if (this.generateWixVars)
586 {
587 varFormat = WixVariableFormat;
588 }
589
590 if (pogName.Equals("Satellites", StringComparison.OrdinalIgnoreCase))
591 {
592 Wix.Directory locDirectory = new Wix.Directory();
593
594 locDirectory.Name = Path.GetFileName(Path.GetDirectoryName(Path.GetFullPath(filePath)));
595 file.Source = String.Concat(String.Format(CultureInfo.InvariantCulture, varFormat, projectName, pogFileSource), "\\", locDirectory.Name, "\\", Path.GetFileName(filePath));
596
597 if (!seenList.ContainsKey(file.Source))
598 {
599 parentDir.AddChild(locDirectory);
600 locDirectory.AddChild(component);
601 component.AddChild(file);
602 seenList.Add(file.Source, true);
603
604 if (this.setUniqueIdentifiers)
605 {
606 locDirectory.Id = this.Core.GenerateIdentifier(DirectoryPrefix, parentDirId, locDirectory.Name);
607 file.Id = this.Core.GenerateIdentifier(FilePrefix, locDirectory.Id, fileName);
608 component.Id = this.Core.GenerateIdentifier(ComponentPrefix, locDirectory.Id, file.Id);
609 }
610 else
611 {
612 locDirectory.Id = this.Core.CreateIdentifierFromFilename(String.Format(DirectoryIdFormat, (parentDir is Wix.DirectoryRef) ? ((Wix.DirectoryRef)parentDir).Id : parentDirId, locDirectory.Name));
613 file.Id = this.Core.CreateIdentifierFromFilename(String.Format(CultureInfo.InvariantCulture, VSProjectHarvester.FileIdFormat, projectName, pogName, String.Concat(locDirectory.Name, ".", fileName)));
614 component.Id = this.Core.CreateIdentifierFromFilename(String.Format(CultureInfo.InvariantCulture, VSProjectHarvester.ComponentIdFormat, projectName, pogName, String.Concat(locDirectory.Name, ".", fileName)));
615 }
616 }
617 }
618 else
619 {
620 file.Source = GenerateSourceFilePath(baseDir, projectName, pogFileSource, filePath, link, varFormat);
621
622 if (!seenList.ContainsKey(file.Source))
623 {
624 component.AddChild(file);
625 parentDir.AddChild(component);
626 seenList.Add(file.Source, true);
627
628 if (this.setUniqueIdentifiers)
629 {
630 file.Id = this.Core.GenerateIdentifier(FilePrefix, parentDirId, fileName);
631 component.Id = this.Core.GenerateIdentifier(ComponentPrefix, parentDirId, file.Id);
632 }
633 else
634 {
635 file.Id = this.Core.CreateIdentifierFromFilename(String.Format(CultureInfo.InvariantCulture, VSProjectHarvester.FileIdFormat, projectName, pogName, fileName));
636 component.Id = this.Core.CreateIdentifierFromFilename(String.Format(CultureInfo.InvariantCulture, VSProjectHarvester.ComponentIdFormat, projectName, pogName, fileName));
637 }
638 }
639 }
640 }
641
642 private void HarvestProjectOutputGroupPackage(string projectName, string pogName, string pogFileSource, string filePath, string fileName, string link, Wix.IParentElement parentDir, Dictionary<string, bool> seenList)
643 {
644 string varFormat = VariableFormat;
645 if (this.generateWixVars)
646 {
647 varFormat = WixVariableFormat;
648 }
649
650 if (pogName.Equals("Binaries", StringComparison.OrdinalIgnoreCase))
651 {
652 if (String.Equals(Path.GetExtension(filePath), ".exe", StringComparison.OrdinalIgnoreCase))
653 {
654 Wix.ExePackage exePackage = new Wix.ExePackage();
655 exePackage.SourceFile = String.Concat(String.Format(CultureInfo.InvariantCulture, varFormat, projectName, pogFileSource), "\\", Path.GetFileName(filePath));
656 if (!seenList.ContainsKey(exePackage.SourceFile))
657 {
658 parentDir.AddChild(exePackage);
659 seenList.Add(exePackage.SourceFile, true);
660 }
661 }
662 else if (String.Equals(Path.GetExtension(filePath), ".msi", StringComparison.OrdinalIgnoreCase))
663 {
664 Wix.MsiPackage msiPackage = new Wix.MsiPackage();
665 msiPackage.SourceFile = String.Concat(String.Format(CultureInfo.InvariantCulture, varFormat, projectName, pogFileSource), "\\", Path.GetFileName(filePath));
666 if (!seenList.ContainsKey(msiPackage.SourceFile))
667 {
668 parentDir.AddChild(msiPackage);
669 seenList.Add(msiPackage.SourceFile, true);
670 }
671 }
672 }
673 }
674
675 private void HarvestProjectOutputGroupPayloadFile(string baseDir, string projectName, string pogName, string pogFileSource, string filePath, string fileName, string link, Wix.IParentElement parentDir, Wix.Payload file, Dictionary<string, bool> seenList)
676 {
677 string varFormat = VariableFormat;
678 if (this.generateWixVars)
679 {
680 varFormat = WixVariableFormat;
681 }
682
683 if (pogName.Equals("Satellites", StringComparison.OrdinalIgnoreCase))
684 {
685 string locDirectoryName = Path.GetFileName(Path.GetDirectoryName(Path.GetFullPath(filePath)));
686 file.SourceFile = String.Concat(String.Format(CultureInfo.InvariantCulture, varFormat, projectName, pogFileSource), "\\", locDirectoryName, "\\", Path.GetFileName(filePath));
687
688 if (!seenList.ContainsKey(file.SourceFile))
689 {
690 parentDir.AddChild(file);
691 seenList.Add(file.SourceFile, true);
692 }
693 }
694 else
695 {
696 file.SourceFile = GenerateSourceFilePath(baseDir, projectName, pogFileSource, filePath, link, varFormat);
697
698 if (!seenList.ContainsKey(file.SourceFile))
699 {
700 parentDir.AddChild(file);
701 seenList.Add(file.SourceFile, true);
702 }
703 }
704 }
705
706 /// <summary>
707 /// Helper function to generates a source file path when harvesting files.
708 /// </summary>
709 /// <param name="baseDir"></param>
710 /// <param name="projectName"></param>
711 /// <param name="pogFileSource"></param>
712 /// <param name="filePath"></param>
713 /// <param name="link"></param>
714 /// <param name="varFormat"></param>
715 /// <returns></returns>
716 private static string GenerateSourceFilePath(string baseDir, string projectName, string pogFileSource, string filePath, string link, string varFormat)
717 {
718 string ret;
719
720 if (null == baseDir && !String.IsNullOrEmpty(link))
721 {
722 // This needs to be the absolute path as a link can be located anywhere.
723 ret = filePath;
724 }
725 else if (null == baseDir)
726 {
727 ret = String.Concat(String.Format(CultureInfo.InvariantCulture, varFormat, projectName, pogFileSource), "\\", Path.GetFileName(filePath));
728 }
729 else if (filePath.StartsWith(baseDir, StringComparison.OrdinalIgnoreCase))
730 {
731 ret = String.Concat(String.Format(CultureInfo.InvariantCulture, varFormat, projectName, pogFileSource), "\\", filePath.Substring(baseDir.Length));
732 }
733 else
734 {
735 // come up with a relative path to the file
736 Uri sourcePathUri = new Uri(filePath);
737 Uri baseDirUri = new Uri(baseDir);
738 Uri sourceRelativeUri = baseDirUri.MakeRelativeUri(sourcePathUri);
739 string relativePath = sourceRelativeUri.ToString().Replace('/', Path.DirectorySeparatorChar);
740 if (!sourceRelativeUri.UserEscaped)
741 {
742 relativePath = Uri.UnescapeDataString(relativePath);
743 }
744
745 ret = String.Concat(String.Format(CultureInfo.InvariantCulture, varFormat, projectName, pogFileSource), "\\", relativePath);
746 }
747
748 return ret;
749 }
750
751 /// <summary>
752 /// Gets a Directory element corresponding to a relative subdirectory within the project,
753 /// either by locating a suitable existing Directory or creating a new one.
754 /// </summary>
755 /// <param name="parentDir">The parent element which the subdirectory is relative to.</param>
756 /// <param name="relativeUri">Relative path of the subdirectory.</param>
757 /// <returns>Directory element for the relative path.</returns>
758 private Wix.IParentElement GetSubDirElement(Wix.IParentElement parentDir, Uri relativeUri)
759 {
760 string[] segments = relativeUri.ToString().Split('\\', '/');
761 string firstSubDirName = Uri.UnescapeDataString(segments[0]);
762 DirectoryAttributeAccessor subDir = null;
763
764 if (String.Equals(firstSubDirName, "..", StringComparison.Ordinal))
765 {
766 return parentDir;
767 }
768
769 Type directoryType;
770 Type directoryRefType;
771 if (parentDir is Wix.Directory || parentDir is Wix.DirectoryRef)
772 {
773 directoryType = typeof(Wix.Directory);
774 directoryRefType = typeof(Wix.DirectoryRef);
775 }
776 else
777 {
778 throw new ArgumentException("GetSubDirElement parentDir");
779 }
780
781 // Search for an existing directory element.
782 foreach (Wix.ISchemaElement childElement in parentDir.Children)
783 {
784 if(VSProjectHarvester.AreTypesEquivalent(childElement.GetType(), directoryType))
785 {
786 DirectoryAttributeAccessor childDir = new DirectoryAttributeAccessor(childElement);
787 if (String.Equals(childDir.Name, firstSubDirName, StringComparison.OrdinalIgnoreCase))
788 {
789 subDir = childDir;
790 break;
791 }
792 }
793 }
794
795 if (subDir == null)
796 {
797 string parentId = null;
798 DirectoryAttributeAccessor parentDirectory = null;
799 DirectoryAttributeAccessor parentDirectoryRef = null;
800
801 if (VSProjectHarvester.AreTypesEquivalent(parentDir.GetType(), directoryType))
802 {
803 parentDirectory = new DirectoryAttributeAccessor((Wix.ISchemaElement)parentDir);
804 }
805 else if (VSProjectHarvester.AreTypesEquivalent(parentDir.GetType(), directoryRefType))
806 {
807 parentDirectoryRef = new DirectoryAttributeAccessor((Wix.ISchemaElement)parentDir);
808 }
809
810 if (parentDirectory != null)
811 {
812 parentId = parentDirectory.Id;
813 }
814 else if (parentDirectoryRef != null)
815 {
816 if (this.setUniqueIdentifiers)
817 {
818 //Use the GUID of the project instead of the project name to help keep things stable.
819 parentId = this.directoryRefSeed;
820 }
821 else
822 {
823 parentId = parentDirectoryRef.Id;
824 }
825 }
826
827 Wix.ISchemaElement newDirectory = (Wix.ISchemaElement)directoryType.GetConstructor(new Type[] { }).Invoke(null);
828 subDir = new DirectoryAttributeAccessor(newDirectory);
829
830 if (this.setUniqueIdentifiers)
831 {
832 subDir.Id = this.Core.GenerateIdentifier(DirectoryPrefix, parentId, firstSubDirName);
833 }
834 else
835 {
836 subDir.Id = String.Format(DirectoryIdFormat, parentId, firstSubDirName);
837 }
838
839 subDir.Name = firstSubDirName;
840
841 parentDir.AddChild(subDir.Element);
842 }
843
844 if (segments.Length == 1)
845 {
846 return subDir.ElementAsParent;
847 }
848 else
849 {
850 Uri nextRelativeUri = new Uri(Uri.UnescapeDataString(relativeUri.ToString()).Substring(firstSubDirName.Length + 1), UriKind.Relative);
851 return this.GetSubDirElement(subDir.ElementAsParent, nextRelativeUri);
852 }
853 }
854
855 private MSBuildProject GetMsbuildProject(string projectFile)
856 {
857 XmlDocument document = new XmlDocument();
858 try
859 {
860 document.Load(projectFile);
861 }
862 catch (Exception e)
863 {
864 throw new WixException(HarvesterErrors.CannotLoadProject(projectFile, e.Message));
865 }
866
867 string version = null;
868
869 if (this.UseToolsVersion)
870 {
871 foreach (XmlNode child in document.ChildNodes)
872 {
873 if (String.Equals(child.Name, "Project", StringComparison.Ordinal) && child.Attributes != null)
874 {
875 XmlNode toolsVersionAttribute = child.Attributes["ToolsVersion"];
876 if (toolsVersionAttribute != null)
877 {
878 version = toolsVersionAttribute.Value;
879 this.Core.Messaging.Write(HarvesterVerboses.FoundToolsVersion(version));
880
881 break;
882 }
883 }
884 }
885
886 switch (version)
887 {
888 case "4.0":
889 version = "4.0.0.0";
890 break;
891 case "12.0":
892 version = "12.0.0.0";
893 break;
894 case "14.0":
895 version = "14.0.0.0";
896 break;
897 default:
898 if (String.IsNullOrEmpty(this.MsbuildBinPath))
899 {
900 throw new WixException(HarvesterErrors.MsbuildBinPathRequired(version ?? "(none)"));
901 }
902
903 version = null;
904 break;
905 }
906 }
907
908 var project = this.ConstructMsbuild40Project(version);
909 return project;
910 }
911
912 private Assembly ResolveFromMsbuildBinPath(object sender, ResolveEventArgs args)
913 {
914 var assemblyName = new AssemblyName(args.Name);
915
916 var assemblyPath = Path.Combine(this.MsbuildBinPath, $"{assemblyName.Name}.dll");
917 if (!File.Exists(assemblyPath))
918 {
919 return null;
920 }
921
922 return Assembly.LoadFrom(assemblyPath);
923 }
924
925 private MSBuildProject ConstructMsbuild40Project(string loadVersion)
926 {
927 const string MSBuildEngineAssemblyName = "Microsoft.Build, Version={0}, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
928 const string MSBuildFrameworkAssemblyName = "Microsoft.Build.Framework, Version={0}, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
929 Assembly msbuildAssembly;
930 Assembly msbuildFrameworkAssembly;
931
932 if (loadVersion == null)
933 {
934 this.Core.Messaging.Write(HarvesterVerboses.LoadingProjectWithBinPath(this.MsbuildBinPath));
935 AppDomain.CurrentDomain.AssemblyResolve += this.ResolveFromMsbuildBinPath;
936
937 try
938 {
939 msbuildAssembly = Assembly.Load("Microsoft.Build");
940 }
941 catch (Exception e)
942 {
943 throw new WixException(HarvesterErrors.CannotLoadMSBuildAssembly(e.Message));
944 }
945
946 try
947 {
948 msbuildFrameworkAssembly = Assembly.Load("Microsoft.Build.Framework");
949 }
950 catch (Exception e)
951 {
952 throw new WixException(HarvesterErrors.CannotLoadMSBuildAssembly(e.Message));
953 }
954 }
955 else
956 {
957 this.Core.Messaging.Write(HarvesterVerboses.LoadingProjectWithVersion(loadVersion));
958
959 try
960 {
961 msbuildAssembly = Assembly.Load(String.Format(MSBuildEngineAssemblyName, loadVersion));
962 }
963 catch (Exception e)
964 {
965 throw new WixException(HarvesterErrors.CannotLoadMSBuildAssembly(e.Message));
966 }
967
968 try
969 {
970 msbuildFrameworkAssembly = Assembly.Load(String.Format(MSBuildFrameworkAssemblyName, loadVersion));
971 }
972 catch (Exception e)
973 {
974 throw new WixException(HarvesterErrors.CannotLoadMSBuildAssembly(e.Message));
975 }
976 }
977
978 Type projectType;
979 Type buildItemType;
980
981 Type buildManagerType;
982 Type buildParametersType;
983 Type buildRequestDataFlagsType;
984 Type buildRequestDataType;
985 Type hostServicesType;
986 Type projectCollectionType;
987 Type projectInstanceType;
988
989 Type writeHandlerType;
990 Type colorSetterType;
991 Type colorResetterType;
992 Type loggerVerbosityType;
993 Type consoleLoggerType;
994 Type iLoggerType;
995
996 try
997 {
998 buildItemType = msbuildAssembly.GetType("Microsoft.Build.Execution.ProjectItemInstance", true);
999 projectType = msbuildAssembly.GetType("Microsoft.Build.Evaluation.Project", true);
1000
1001 buildManagerType = msbuildAssembly.GetType("Microsoft.Build.Execution.BuildManager", true);
1002 buildParametersType = msbuildAssembly.GetType("Microsoft.Build.Execution.BuildParameters", true);
1003 buildRequestDataFlagsType = msbuildAssembly.GetType("Microsoft.Build.Execution.BuildRequestDataFlags", true);
1004 buildRequestDataType = msbuildAssembly.GetType("Microsoft.Build.Execution.BuildRequestData", true);
1005 hostServicesType = msbuildAssembly.GetType("Microsoft.Build.Execution.HostServices", true);
1006 projectCollectionType = msbuildAssembly.GetType("Microsoft.Build.Evaluation.ProjectCollection", true);
1007 projectInstanceType = msbuildAssembly.GetType("Microsoft.Build.Execution.ProjectInstance", true);
1008
1009 writeHandlerType = msbuildAssembly.GetType("Microsoft.Build.Logging.WriteHandler", true);
1010 colorSetterType = msbuildAssembly.GetType("Microsoft.Build.Logging.ColorSetter", true);
1011 colorResetterType = msbuildAssembly.GetType("Microsoft.Build.Logging.ColorResetter", true);
1012 loggerVerbosityType = msbuildFrameworkAssembly.GetType("Microsoft.Build.Framework.LoggerVerbosity", true);
1013 consoleLoggerType = msbuildAssembly.GetType("Microsoft.Build.Logging.ConsoleLogger", true);
1014 iLoggerType = msbuildFrameworkAssembly.GetType("Microsoft.Build.Framework.ILogger", true);
1015 }
1016 catch (TargetInvocationException tie)
1017 {
1018 throw new WixException(HarvesterErrors.CannotLoadMSBuildEngine(tie.InnerException.Message));
1019 }
1020 catch (Exception e)
1021 {
1022 throw new WixException(HarvesterErrors.CannotLoadMSBuildEngine(e.Message));
1023 }
1024
1025 MSBuild40Types types = new MSBuild40Types();
1026 types.buildManagerType = buildManagerType;
1027 types.buildParametersType = buildParametersType;
1028 types.buildRequestDataFlagsType = buildRequestDataFlagsType;
1029 types.buildRequestDataType = buildRequestDataType;
1030 types.hostServicesType = hostServicesType;
1031 types.projectCollectionType = projectCollectionType;
1032 types.projectInstanceType = projectInstanceType;
1033 types.writeHandlerType = writeHandlerType;
1034 types.colorSetterType = colorSetterType;
1035 types.colorResetterType = colorResetterType;
1036 types.loggerVerbosityType = loggerVerbosityType;
1037 types.consoleLoggerType = consoleLoggerType;
1038 types.iLoggerType = iLoggerType;
1039 return new MSBuild40Project(null, projectType, buildItemType, loadVersion, types, this.Core, this.configuration, this.platform);
1040 }
1041
1042 private static bool AreTypesEquivalent(Type a, Type b)
1043 {
1044 return (a == b) || (a.IsAssignableFrom(b) && b.IsAssignableFrom(a));
1045 }
1046
1047 private abstract class MSBuildProject
1048 {
1049 protected Type projectType;
1050 protected Type buildItemType;
1051 protected object project;
1052 private string loadVersion;
1053
1054 public MSBuildProject(object project, Type projectType, Type buildItemType, string loadVersion)
1055 {
1056 this.project = project;
1057 this.projectType = projectType;
1058 this.buildItemType = buildItemType;
1059 this.loadVersion = loadVersion;
1060 }
1061
1062 public string LoadVersion
1063 {
1064 get { return this.loadVersion; }
1065 }
1066
1067 public abstract bool Build(string projectFileName, string[] targetNames, IDictionary targetOutputs);
1068
1069 public abstract MSBuildProjectItemType GetBuildItem(object buildItem);
1070
1071 public abstract IEnumerable GetEvaluatedItemsByName(string itemName);
1072
1073 public abstract string GetEvaluatedProperty(string propertyName);
1074
1075 public abstract void Load(string projectFileName);
1076 }
1077
1078 private abstract class MSBuildProjectItemType
1079 {
1080 public MSBuildProjectItemType(object buildItem)
1081 {
1082 this.buildItem = buildItem;
1083 }
1084
1085 public abstract override string ToString();
1086
1087 public abstract string GetMetadata(string name);
1088
1089 protected object buildItem;
1090 }
1091
1092
1093 private struct MSBuild40Types
1094 {
1095 public Type buildManagerType;
1096 public Type buildParametersType;
1097 public Type buildRequestDataFlagsType;
1098 public Type buildRequestDataType;
1099 public Type hostServicesType;
1100 public Type projectCollectionType;
1101 public Type projectInstanceType;
1102 public Type writeHandlerType;
1103 public Type colorSetterType;
1104 public Type colorResetterType;
1105 public Type loggerVerbosityType;
1106 public Type consoleLoggerType;
1107 public Type iLoggerType;
1108 }
1109
1110 private class MSBuild40Project : MSBuildProject
1111 {
1112 private MSBuild40Types types;
1113 private object projectCollection;
1114 private object currentProjectInstance;
1115 private object buildManager;
1116 private object buildParameters;
1117 private IHarvesterCore harvesterCore;
1118
1119 public MSBuild40Project(object project, Type projectType, Type buildItemType, string loadVersion, MSBuild40Types types, IHarvesterCore harvesterCore, string configuration, string platform)
1120 : base(project, projectType, buildItemType, loadVersion)
1121 {
1122 this.types = types;
1123 this.harvesterCore = harvesterCore;
1124
1125 this.buildParameters = this.types.buildParametersType.GetConstructor(new Type[] { }).Invoke(null);
1126
1127 try
1128 {
1129 var loggers = this.CreateLoggers();
1130
1131 // this.buildParameters.Loggers = loggers;
1132 this.types.buildParametersType.GetProperty("Loggers").SetValue(this.buildParameters, loggers, null);
1133 }
1134 catch (TargetInvocationException tie)
1135 {
1136 if (this.harvesterCore != null)
1137 {
1138 this.harvesterCore.Messaging.Write(HarvesterWarnings.NoLogger(tie.InnerException.Message));
1139 }
1140 }
1141 catch (Exception e)
1142 {
1143 if (this.harvesterCore != null)
1144 {
1145 this.harvesterCore.Messaging.Write(HarvesterWarnings.NoLogger(e.Message));
1146 }
1147 }
1148
1149 this.buildManager = this.types.buildManagerType.GetConstructor(new Type[] { }).Invoke(null);
1150
1151 if (configuration != null || platform != null)
1152 {
1153 Dictionary<string, string> globalVariables = new Dictionary<string, string>();
1154 if (configuration != null)
1155 {
1156 globalVariables.Add("Configuration", configuration);
1157 }
1158
1159 if (platform != null)
1160 {
1161 globalVariables.Add("Platform", platform);
1162 }
1163
1164 this.projectCollection = this.types.projectCollectionType.GetConstructor(new Type[] { typeof(IDictionary<string, string>) }).Invoke(new object[] { globalVariables });
1165 }
1166 else
1167 {
1168 this.projectCollection = this.types.projectCollectionType.GetConstructor(new Type[] {}).Invoke(null);
1169 }
1170 }
1171
1172 private object CreateLoggers()
1173 {
1174 var logger = new HarvestLogger(this.harvesterCore.Messaging);
1175 var loggerVerbosity = Enum.Parse(this.types.loggerVerbosityType, "Minimal");
1176 var writeHandler = Delegate.CreateDelegate(this.types.writeHandlerType, logger, nameof(logger.LogMessage));
1177 var colorSetter = Delegate.CreateDelegate(this.types.colorSetterType, logger, nameof(logger.SetColor));
1178 var colorResetter = Delegate.CreateDelegate(this.types.colorResetterType, logger, nameof(logger.ResetColor));
1179
1180 var consoleLoggerCtor = this.types.consoleLoggerType.GetConstructor(new Type[] {
1181 this.types.loggerVerbosityType,
1182 this.types.writeHandlerType,
1183 this.types.colorSetterType,
1184 this.types.colorResetterType,
1185 });
1186 var consoleLogger = consoleLoggerCtor.Invoke(new object[] { loggerVerbosity, writeHandler, colorSetter, colorResetter });
1187
1188 var loggers = Array.CreateInstance(this.types.iLoggerType, 1);
1189 loggers.SetValue(consoleLogger, 0);
1190
1191 return loggers;
1192 }
1193
1194 public override bool Build(string projectFileName, string[] targetNames, IDictionary targetOutputs)
1195 {
1196 try
1197 {
1198 // this.buildManager.BeginBuild(this.buildParameters);
1199 this.types.buildManagerType.GetMethod("BeginBuild", new Type[] { this.types.buildParametersType }).Invoke(this.buildManager, new object[] { this.buildParameters });
1200
1201 // buildRequestData = new BuildRequestData(this.currentProjectInstance, targetNames, null, BuildRequestData.BuildRequestDataFlags.ReplaceExistingProjectInstance);
1202 ConstructorInfo buildRequestDataCtor = this.types.buildRequestDataType.GetConstructor(
1203 new Type[]
1204 {
1205 this.types.projectInstanceType, typeof(string[]), this.types.hostServicesType, this.types.buildRequestDataFlagsType
1206 });
1207 object buildRequestDataFlags = this.types.buildRequestDataFlagsType.GetField("ReplaceExistingProjectInstance").GetRawConstantValue();
1208 object buildRequestData = buildRequestDataCtor.Invoke(new object[] { this.currentProjectInstance, targetNames, null, buildRequestDataFlags });
1209
1210 // BuildSubmission submission = this.buildManager.PendBuildRequest(buildRequestData);
1211 object submission = this.types.buildManagerType.GetMethod("PendBuildRequest", new Type[] { this.types.buildRequestDataType })
1212 .Invoke(this.buildManager, new object[] { buildRequestData });
1213
1214 // BuildResult buildResult = submission.Execute();
1215 object buildResult = submission.GetType().GetMethod("Execute", new Type[] { }).Invoke(submission, null);
1216
1217 // bool buildSucceeded = buildResult.OverallResult == BuildResult.Success;
1218 object overallResult = buildResult.GetType().GetProperty("OverallResult").GetValue(buildResult, null);
1219 bool buildSucceeded = String.Equals(overallResult.ToString(), "Success", StringComparison.Ordinal);
1220
1221 // this.buildManager.EndBuild();
1222 this.types.buildManagerType.GetMethod("EndBuild", new Type[] { }).Invoke(this.buildManager, null);
1223
1224 // fill in empty lists for each target so that heat will look at the item group later
1225 foreach (string target in targetNames)
1226 {
1227 targetOutputs.Add(target, new List<object>());
1228 }
1229
1230 return buildSucceeded;
1231 }
1232 catch (TargetInvocationException tie)
1233 {
1234 throw new WixException(HarvesterErrors.CannotBuildProject(projectFileName, tie.InnerException.Message));
1235 }
1236 catch (Exception e)
1237 {
1238 throw new WixException(HarvesterErrors.CannotBuildProject(projectFileName, e.Message));
1239 }
1240 }
1241
1242 public override MSBuildProjectItemType GetBuildItem(object buildItem)
1243 {
1244 return new MSBuild40ProjectItemType(buildItem);
1245 }
1246
1247 public override IEnumerable GetEvaluatedItemsByName(string itemName)
1248 {
1249 MethodInfo getEvaluatedItem = this.types.projectInstanceType.GetMethod("GetItems", new Type[] { typeof(string) });
1250 return (IEnumerable)getEvaluatedItem.Invoke(this.currentProjectInstance, new object[] { itemName });
1251 }
1252
1253 public override string GetEvaluatedProperty(string propertyName)
1254 {
1255 MethodInfo getProperty = this.types.projectInstanceType.GetMethod("GetPropertyValue", new Type[] { typeof(string) });
1256 return (string)getProperty.Invoke(this.currentProjectInstance, new object[] { propertyName });
1257 }
1258
1259 public override void Load(string projectFileName)
1260 {
1261 try
1262 {
1263 //this.project = this.projectCollection.LoadProject(projectFileName);
1264 this.project = this.types.projectCollectionType.GetMethod("LoadProject", new Type[] { typeof(string) }).Invoke(this.projectCollection, new object[] { projectFileName });
1265
1266 // this.currentProjectInstance = this.project.CreateProjectInstance();
1267 MethodInfo createProjectInstanceMethod = this.projectType.GetMethod("CreateProjectInstance", new Type[] { });
1268 this.currentProjectInstance = createProjectInstanceMethod.Invoke(this.project, null);
1269 }
1270 catch (TargetInvocationException tie)
1271 {
1272 throw new WixException(HarvesterErrors.CannotLoadProject(projectFileName, tie.InnerException.Message));
1273 }
1274 catch (Exception e)
1275 {
1276 throw new WixException(HarvesterErrors.CannotLoadProject(projectFileName, e.Message));
1277 }
1278 }
1279 }
1280
1281 private class MSBuild40ProjectItemType : MSBuildProjectItemType
1282 {
1283 public MSBuild40ProjectItemType(object buildItem)
1284 : base(buildItem)
1285 {
1286 }
1287
1288 public override string ToString()
1289 {
1290 PropertyInfo includeProperty = this.buildItem.GetType().GetProperty("EvaluatedInclude");
1291 return (string)includeProperty.GetValue(this.buildItem, null);
1292 }
1293
1294 public override string GetMetadata(string name)
1295 {
1296 MethodInfo getMetadataMethod = this.buildItem.GetType().GetMethod("GetMetadataValue");
1297 if (null != getMetadataMethod)
1298 {
1299 return (string)getMetadataMethod.Invoke(this.buildItem, new object[] { name });
1300 }
1301 return string.Empty;
1302 }
1303 }
1304
1305 /// <summary>
1306 /// Used internally in the VSProjectHarvester class to encapsulate
1307 /// the settings for a particular MSBuild "project output group".
1308 /// </summary>
1309 private struct ProjectOutputGroup
1310 {
1311 public readonly string Name;
1312 public readonly string BuildOutputGroup;
1313 public readonly string FileSource;
1314
1315 /// <summary>
1316 /// Creates a new project output group.
1317 /// </summary>
1318 /// <param name="name">Friendly name used by heat.</param>
1319 /// <param name="buildOutputGroup">MSBuild's name of the project output group.</param>
1320 /// <param name="fileSource">VS directory token containing the files of the POG.</param>
1321 public ProjectOutputGroup(string name, string buildOutputGroup, string fileSource)
1322 {
1323 this.Name = name;
1324 this.BuildOutputGroup = buildOutputGroup;
1325 this.FileSource = fileSource;
1326 }
1327 }
1328
1329 /// <summary>
1330 /// Internal class for getting and setting common attrbiutes on
1331 /// directory elements.
1332 /// </summary>
1333 internal class DirectoryAttributeAccessor
1334 {
1335 public Wix.ISchemaElement directoryElement;
1336
1337 public DirectoryAttributeAccessor(Wix.ISchemaElement directoryElement)
1338 {
1339 this.directoryElement = directoryElement;
1340 }
1341
1342 /// <summary>
1343 /// Gets the element as a ISchemaElement.
1344 /// </summary>
1345 public Wix.ISchemaElement Element
1346 {
1347 get { return this.directoryElement; }
1348 }
1349
1350 /// <summary>
1351 /// Gets the element as a IParentElement.
1352 /// </summary>
1353 public Wix.IParentElement ElementAsParent
1354 {
1355 get { return (Wix.IParentElement)this.directoryElement; }
1356 }
1357
1358 /// <summary>
1359 /// Gets or sets the Id attrbiute.
1360 /// </summary>
1361 public string Id
1362 {
1363 get
1364 {
1365 if (this.directoryElement is Wix.Directory wixDirectory)
1366 {
1367 return wixDirectory.Id;
1368 }
1369 else if (this.directoryElement is Wix.DirectoryRef wixDirectoryRef)
1370 {
1371 return wixDirectoryRef.Id;
1372 }
1373 else
1374 {
1375 throw new WixException(HarvesterErrors.DirectoryAttributeAccessorBadType("Id"));
1376 }
1377 }
1378 set
1379 {
1380 if (this.directoryElement is Wix.Directory wixDirectory)
1381 {
1382 wixDirectory.Id = value;
1383 }
1384 else if (this.directoryElement is Wix.DirectoryRef wixDirectoryRef)
1385 {
1386 wixDirectoryRef.Id = value;
1387 }
1388 else
1389 {
1390 throw new WixException(HarvesterErrors.DirectoryAttributeAccessorBadType("Id"));
1391 }
1392 }
1393 }
1394
1395 /// <summary>
1396 /// Gets or sets the Name attribute.
1397 /// </summary>
1398 public string Name
1399 {
1400 get
1401 {
1402 if (this.directoryElement is Wix.Directory wixDirectory)
1403 {
1404 return wixDirectory.Name;
1405 }
1406 else
1407 {
1408 throw new WixException(HarvesterErrors.DirectoryAttributeAccessorBadType("Name"));
1409 }
1410 }
1411 set
1412 {
1413 if (this.directoryElement is Wix.Directory wixDirectory)
1414 {
1415 wixDirectory.Name = value;
1416 }
1417 else
1418 {
1419 throw new WixException(HarvesterErrors.DirectoryAttributeAccessorBadType("Name"));
1420 }
1421 }
1422 }
1423 }
1424
1425 internal class HarvestLogger
1426 {
1427 public HarvestLogger(IMessaging messaging)
1428 {
1429 this.Color = ConsoleColor.Black;
1430 this.Messaging = messaging;
1431 }
1432
1433 private ConsoleColor Color { get; set; }
1434 private IMessaging Messaging { get; }
1435
1436 public void LogMessage(string message)
1437 {
1438 if (this.Color == ConsoleColor.Red)
1439 {
1440 this.Messaging.Write(HarvesterErrors.BuildErrorDuringHarvesting(message));
1441 }
1442 }
1443
1444 public void SetColor(ConsoleColor color)
1445 {
1446 this.Color = color;
1447 }
1448
1449 public void ResetColor()
1450 {
1451 this.Color = ConsoleColor.Black;
1452 }
1453 }
1454 }
1455}