aboutsummaryrefslogtreecommitdiff
path: root/src/tools/heat/VSProjectHarvester.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/heat/VSProjectHarvester.cs')
-rw-r--r--src/tools/heat/VSProjectHarvester.cs1457
1 files changed, 0 insertions, 1457 deletions
diff --git a/src/tools/heat/VSProjectHarvester.cs b/src/tools/heat/VSProjectHarvester.cs
deleted file mode 100644
index 92eac6ff..00000000
--- a/src/tools/heat/VSProjectHarvester.cs
+++ /dev/null
@@ -1,1457 +0,0 @@
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 string directoryRefId;
338
339 if (!String.IsNullOrEmpty(this.directoryIds))
340 {
341 directoryRefId = this.directoryIds;
342 }
343 else if (this.setUniqueIdentifiers)
344 {
345 directoryRefId = String.Format(CultureInfo.InvariantCulture, DirectoryIdFormat, sanitizedProjectName, pog.Name);
346 }
347 else
348 {
349 directoryRefId = this.Core.CreateIdentifierFromFilename(String.Format(CultureInfo.InvariantCulture, DirectoryIdFormat, sanitizedProjectName, pog.Name));
350 }
351
352 var directoryRef = DirectoryHelper.CreateDirectoryReference(directoryRefId);
353 harvestParent = directoryRef;
354
355 this.directoryRefSeed = this.Core.GenerateIdentifier(DirectoryPrefix, this.projectGUID, pog.Name);
356 }
357
358 IEnumerable pogFiles = buildOutputs[pog.BuildOutputGroup] as IEnumerable;
359 if (pogFiles == null)
360 {
361 throw new WixException(HarvesterErrors.MissingProjectOutputGroup(
362 projectFile, pog.BuildOutputGroup));
363 }
364
365 if (pog.FileSource == "ProjectDir")
366 {
367 projectBaseDir = Path.GetDirectoryName(projectFile) + "\\";
368 }
369
370 int harvestCount = this.HarvestProjectOutputGroupFiles(projectBaseDir, sanitizedProjectName, pog.Name, pog.FileSource, pogFiles, harvestParent);
371
372 if (this.GenerateType == GenerateType.Container)
373 {
374 // harvestParent must be a Container at this point
375 Wix.Container container = harvestParent as Wix.Container;
376
377 Wix.Fragment fragment = new Wix.Fragment();
378 fragment.AddChild(container);
379 fragmentList.Add(fragment);
380 }
381 else if (this.GenerateType == GenerateType.PackageGroup)
382 {
383 // harvestParent must be a PackageGroup at this point
384 Wix.PackageGroup packageGroup = harvestParent as Wix.PackageGroup;
385
386 Wix.Fragment fragment = new Wix.Fragment();
387 fragment.AddChild(packageGroup);
388 fragmentList.Add(fragment);
389 }
390 else if (this.GenerateType == GenerateType.PayloadGroup)
391 {
392 // harvestParent must be a Container at this point
393 Wix.PayloadGroup payloadGroup = harvestParent as Wix.PayloadGroup;
394
395 Wix.Fragment fragment = new Wix.Fragment();
396 fragment.AddChild(payloadGroup);
397 fragmentList.Add(fragment);
398 }
399 else
400 {
401 // harvestParent must be a DirectoryRef at this point
402 Wix.DirectoryRef directoryRef = harvestParent as Wix.DirectoryRef;
403
404 if (harvestCount > 0)
405 {
406 Wix.Fragment drf = new Wix.Fragment();
407 drf.AddChild(directoryRef);
408 fragmentList.Add(drf);
409 }
410
411 Wix.ComponentGroup cg = new Wix.ComponentGroup();
412
413 if (this.setUniqueIdentifiers || !String.IsNullOrEmpty(this.directoryIds))
414 {
415 cg.Id = String.Format(CultureInfo.InvariantCulture, DirectoryIdFormat, sanitizedProjectName, pog.Name);
416 }
417 else
418 {
419 cg.Id = directoryRef.Id;
420 }
421
422 if (harvestCount > 0)
423 {
424 this.AddComponentsToComponentGroup(directoryRef, cg);
425 }
426
427 Wix.Fragment cgf = new Wix.Fragment();
428 cgf.AddChild(cg);
429 fragmentList.Add(cgf);
430 }
431
432 return harvestCount;
433 }
434
435 /// <summary>
436 /// Add all Components in an element tree to a ComponentGroup.
437 /// </summary>
438 /// <param name="parent">Parent of an element tree that will be searched for Components.</param>
439 /// <param name="cg">The ComponentGroup the Components will be added to.</param>
440 private void AddComponentsToComponentGroup(Wix.IParentElement parent, Wix.ComponentGroup cg)
441 {
442 foreach (Wix.ISchemaElement childElement in parent.Children)
443 {
444 Wix.Component c = childElement as Wix.Component;
445 if (c != null)
446 {
447 Wix.ComponentRef cr = new Wix.ComponentRef();
448 cr.Id = c.Id;
449 cg.AddChild(cr);
450 }
451 else
452 {
453 Wix.IParentElement p = childElement as Wix.IParentElement;
454 if (p != null)
455 {
456 this.AddComponentsToComponentGroup(p, cg);
457 }
458 }
459 }
460 }
461
462 /// <summary>
463 /// Harvest files from one output group of a VS project.
464 /// </summary>
465 /// <param name="baseDir">The base directory of the files.</param>
466 /// <param name="projectName">Name of the project, to be used as a prefix for generated identifiers.</param>
467 /// <param name="pogName">Name of the project output group, used for generating identifiers for WiX elements.</param>
468 /// <param name="pogFileSource">The ProjectOutputGroup file source.</param>
469 /// <param name="outputGroupFiles">The files from one output group to harvest.</param>
470 /// <param name="parent">The parent element that will contain the components of the harvested files.</param>
471 /// <returns>The number of files harvested.</returns>
472 private int HarvestProjectOutputGroupFiles(string baseDir, string projectName, string pogName, string pogFileSource, IEnumerable outputGroupFiles, Wix.IParentElement parent)
473 {
474 int fileCount = 0;
475
476 Wix.ISchemaElement exeFile = null;
477 Wix.ISchemaElement dllFile = null;
478 Wix.ISchemaElement appConfigFile = null;
479
480 // Keep track of files inserted
481 // Files can have different absolute paths but get mapped to the same SourceFile
482 // after the project variables have been used. For example, a WiX project that
483 // is building multiple cultures will have many output MSIs/MSMs, but will all get
484 // mapped to $(var.ProjName.TargetDir)\ProjName.msm. These duplicates would
485 // prevent generated code from compiling.
486 Dictionary<string, bool> seenList = new Dictionary<string,bool>();
487
488 foreach (object output in outputGroupFiles)
489 {
490 string filePath = output.ToString();
491 string fileName = Path.GetFileName(filePath);
492 string fileDir = Path.GetDirectoryName(filePath);
493 string link = null;
494
495 MethodInfo getMetadataMethod = output.GetType().GetMethod("GetMetadata");
496 if (getMetadataMethod != null)
497 {
498 link = (string)getMetadataMethod.Invoke(output, new object[] { "Link" });
499 if (!String.IsNullOrEmpty(link))
500 {
501 fileDir = Path.GetDirectoryName(Path.Combine(baseDir, link));
502 }
503 }
504
505 Wix.IParentElement parentDir = parent;
506 // Ignore Containers and PayloadGroups because they do not have a nested structure.
507 if (baseDir != null && !String.Equals(Path.GetDirectoryName(baseDir), fileDir, StringComparison.OrdinalIgnoreCase)
508 && this.GenerateType != GenerateType.Container && this.GenerateType != GenerateType.PackageGroup && this.GenerateType != GenerateType.PayloadGroup)
509 {
510 Uri baseUri = new Uri(baseDir);
511 Uri relativeUri = baseUri.MakeRelativeUri(new Uri(fileDir));
512 parentDir = this.GetSubDirElement(parentDir, relativeUri);
513 }
514
515 string parentDirId = null;
516
517 if (parentDir is Wix.DirectoryRef)
518 {
519 parentDirId = this.directoryRefSeed;
520 }
521 else if (parentDir is Wix.Directory)
522 {
523 parentDirId = ((Wix.Directory)parentDir).Id;
524 }
525
526 if (this.GenerateType == GenerateType.Container || this.GenerateType == GenerateType.PayloadGroup)
527 {
528 Wix.Payload payload = new Wix.Payload();
529
530 this.HarvestProjectOutputGroupPayloadFile(baseDir, projectName, pogName, pogFileSource, filePath, fileName, link, parentDir, payload, seenList);
531 }
532 else if (this.GenerateType == GenerateType.PackageGroup)
533 {
534 this.HarvestProjectOutputGroupPackage(projectName, pogName, pogFileSource, filePath, fileName, link, parentDir, seenList);
535 }
536 else
537 {
538 Wix.Component component = new Wix.Component();
539 Wix.File file = new Wix.File();
540
541 this.HarvestProjectOutputGroupFile(baseDir, projectName, pogName, pogFileSource, filePath, fileName, link, parentDir, parentDirId, component, file, seenList);
542
543 if (String.Equals(Path.GetExtension(file.Source), ".exe", StringComparison.OrdinalIgnoreCase))
544 {
545 exeFile = file;
546 }
547 else if (String.Equals(Path.GetExtension(file.Source), ".dll", StringComparison.OrdinalIgnoreCase))
548 {
549 dllFile = file;
550 }
551 else if (file.Source.EndsWith("app.config", StringComparison.OrdinalIgnoreCase))
552 {
553 appConfigFile = file;
554 }
555 }
556
557 fileCount++;
558 }
559
560 // if there was no exe file found fallback on the dll file found
561 if (exeFile == null && dllFile != null)
562 {
563 exeFile = dllFile;
564 }
565
566 // Special case for the app.config file in the Binaries POG...
567 // The POG refers to the files in the OBJ directory, while the
568 // generated WiX code references them in the bin directory.
569 // The app.config file gets renamed to match the exe name.
570 if ("Binaries" == pogName && null != exeFile && null != appConfigFile)
571 {
572 if (appConfigFile is Wix.File)
573 {
574 Wix.File appConfigFileAsWixFile = appConfigFile as Wix.File;
575 Wix.File exeFileAsWixFile = exeFile as Wix.File;
576 // Case insensitive replace
577 appConfigFileAsWixFile.Source = Regex.Replace(appConfigFileAsWixFile.Source, @"app\.config", Path.GetFileName(exeFileAsWixFile.Source) + ".config", RegexOptions.IgnoreCase);
578 }
579 }
580
581 return fileCount;
582 }
583
584 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)
585 {
586 string varFormat = VariableFormat;
587 if (this.generateWixVars)
588 {
589 varFormat = WixVariableFormat;
590 }
591
592 if (pogName.Equals("Satellites", StringComparison.OrdinalIgnoreCase))
593 {
594 Wix.Directory locDirectory = new Wix.Directory();
595
596 locDirectory.Name = Path.GetFileName(Path.GetDirectoryName(Path.GetFullPath(filePath)));
597 file.Source = String.Concat(String.Format(CultureInfo.InvariantCulture, varFormat, projectName, pogFileSource), "\\", locDirectory.Name, "\\", Path.GetFileName(filePath));
598
599 if (!seenList.ContainsKey(file.Source))
600 {
601 parentDir.AddChild(locDirectory);
602 locDirectory.AddChild(component);
603 component.AddChild(file);
604 seenList.Add(file.Source, true);
605
606 if (this.setUniqueIdentifiers)
607 {
608 locDirectory.Id = this.Core.GenerateIdentifier(DirectoryPrefix, parentDirId, locDirectory.Name);
609 file.Id = this.Core.GenerateIdentifier(FilePrefix, locDirectory.Id, fileName);
610 component.Id = this.Core.GenerateIdentifier(ComponentPrefix, locDirectory.Id, file.Id);
611 }
612 else
613 {
614 locDirectory.Id = this.Core.CreateIdentifierFromFilename(String.Format(DirectoryIdFormat, (parentDir is Wix.DirectoryRef) ? ((Wix.DirectoryRef)parentDir).Id : parentDirId, locDirectory.Name));
615 file.Id = this.Core.CreateIdentifierFromFilename(String.Format(CultureInfo.InvariantCulture, VSProjectHarvester.FileIdFormat, projectName, pogName, String.Concat(locDirectory.Name, ".", fileName)));
616 component.Id = this.Core.CreateIdentifierFromFilename(String.Format(CultureInfo.InvariantCulture, VSProjectHarvester.ComponentIdFormat, projectName, pogName, String.Concat(locDirectory.Name, ".", fileName)));
617 }
618 }
619 }
620 else
621 {
622 file.Source = GenerateSourceFilePath(baseDir, projectName, pogFileSource, filePath, link, varFormat);
623
624 if (!seenList.ContainsKey(file.Source))
625 {
626 component.AddChild(file);
627 parentDir.AddChild(component);
628 seenList.Add(file.Source, true);
629
630 if (this.setUniqueIdentifiers)
631 {
632 file.Id = this.Core.GenerateIdentifier(FilePrefix, parentDirId, fileName);
633 component.Id = this.Core.GenerateIdentifier(ComponentPrefix, parentDirId, file.Id);
634 }
635 else
636 {
637 file.Id = this.Core.CreateIdentifierFromFilename(String.Format(CultureInfo.InvariantCulture, VSProjectHarvester.FileIdFormat, projectName, pogName, fileName));
638 component.Id = this.Core.CreateIdentifierFromFilename(String.Format(CultureInfo.InvariantCulture, VSProjectHarvester.ComponentIdFormat, projectName, pogName, fileName));
639 }
640 }
641 }
642 }
643
644 private void HarvestProjectOutputGroupPackage(string projectName, string pogName, string pogFileSource, string filePath, string fileName, string link, Wix.IParentElement parentDir, Dictionary<string, bool> seenList)
645 {
646 string varFormat = VariableFormat;
647 if (this.generateWixVars)
648 {
649 varFormat = WixVariableFormat;
650 }
651
652 if (pogName.Equals("Binaries", StringComparison.OrdinalIgnoreCase))
653 {
654 if (String.Equals(Path.GetExtension(filePath), ".exe", StringComparison.OrdinalIgnoreCase))
655 {
656 Wix.ExePackage exePackage = new Wix.ExePackage();
657 exePackage.SourceFile = String.Concat(String.Format(CultureInfo.InvariantCulture, varFormat, projectName, pogFileSource), "\\", Path.GetFileName(filePath));
658 if (!seenList.ContainsKey(exePackage.SourceFile))
659 {
660 parentDir.AddChild(exePackage);
661 seenList.Add(exePackage.SourceFile, true);
662 }
663 }
664 else if (String.Equals(Path.GetExtension(filePath), ".msi", StringComparison.OrdinalIgnoreCase))
665 {
666 Wix.MsiPackage msiPackage = new Wix.MsiPackage();
667 msiPackage.SourceFile = String.Concat(String.Format(CultureInfo.InvariantCulture, varFormat, projectName, pogFileSource), "\\", Path.GetFileName(filePath));
668 if (!seenList.ContainsKey(msiPackage.SourceFile))
669 {
670 parentDir.AddChild(msiPackage);
671 seenList.Add(msiPackage.SourceFile, true);
672 }
673 }
674 }
675 }
676
677 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)
678 {
679 string varFormat = VariableFormat;
680 if (this.generateWixVars)
681 {
682 varFormat = WixVariableFormat;
683 }
684
685 if (pogName.Equals("Satellites", StringComparison.OrdinalIgnoreCase))
686 {
687 string locDirectoryName = Path.GetFileName(Path.GetDirectoryName(Path.GetFullPath(filePath)));
688 file.SourceFile = String.Concat(String.Format(CultureInfo.InvariantCulture, varFormat, projectName, pogFileSource), "\\", locDirectoryName, "\\", Path.GetFileName(filePath));
689
690 if (!seenList.ContainsKey(file.SourceFile))
691 {
692 parentDir.AddChild(file);
693 seenList.Add(file.SourceFile, true);
694 }
695 }
696 else
697 {
698 file.SourceFile = GenerateSourceFilePath(baseDir, projectName, pogFileSource, filePath, link, varFormat);
699
700 if (!seenList.ContainsKey(file.SourceFile))
701 {
702 parentDir.AddChild(file);
703 seenList.Add(file.SourceFile, true);
704 }
705 }
706 }
707
708 /// <summary>
709 /// Helper function to generates a source file path when harvesting files.
710 /// </summary>
711 /// <param name="baseDir"></param>
712 /// <param name="projectName"></param>
713 /// <param name="pogFileSource"></param>
714 /// <param name="filePath"></param>
715 /// <param name="link"></param>
716 /// <param name="varFormat"></param>
717 /// <returns></returns>
718 private static string GenerateSourceFilePath(string baseDir, string projectName, string pogFileSource, string filePath, string link, string varFormat)
719 {
720 string ret;
721
722 if (null == baseDir && !String.IsNullOrEmpty(link))
723 {
724 // This needs to be the absolute path as a link can be located anywhere.
725 ret = filePath;
726 }
727 else if (null == baseDir)
728 {
729 ret = String.Concat(String.Format(CultureInfo.InvariantCulture, varFormat, projectName, pogFileSource), "\\", Path.GetFileName(filePath));
730 }
731 else if (filePath.StartsWith(baseDir, StringComparison.OrdinalIgnoreCase))
732 {
733 ret = String.Concat(String.Format(CultureInfo.InvariantCulture, varFormat, projectName, pogFileSource), "\\", filePath.Substring(baseDir.Length));
734 }
735 else
736 {
737 // come up with a relative path to the file
738 Uri sourcePathUri = new Uri(filePath);
739 Uri baseDirUri = new Uri(baseDir);
740 Uri sourceRelativeUri = baseDirUri.MakeRelativeUri(sourcePathUri);
741 string relativePath = sourceRelativeUri.ToString().Replace('/', Path.DirectorySeparatorChar);
742 if (!sourceRelativeUri.UserEscaped)
743 {
744 relativePath = Uri.UnescapeDataString(relativePath);
745 }
746
747 ret = String.Concat(String.Format(CultureInfo.InvariantCulture, varFormat, projectName, pogFileSource), "\\", relativePath);
748 }
749
750 return ret;
751 }
752
753 /// <summary>
754 /// Gets a Directory element corresponding to a relative subdirectory within the project,
755 /// either by locating a suitable existing Directory or creating a new one.
756 /// </summary>
757 /// <param name="parentDir">The parent element which the subdirectory is relative to.</param>
758 /// <param name="relativeUri">Relative path of the subdirectory.</param>
759 /// <returns>Directory element for the relative path.</returns>
760 private Wix.IParentElement GetSubDirElement(Wix.IParentElement parentDir, Uri relativeUri)
761 {
762 string[] segments = relativeUri.ToString().Split('\\', '/');
763 string firstSubDirName = Uri.UnescapeDataString(segments[0]);
764 DirectoryAttributeAccessor subDir = null;
765
766 if (String.Equals(firstSubDirName, "..", StringComparison.Ordinal))
767 {
768 return parentDir;
769 }
770
771 Type directoryType;
772 Type directoryRefType;
773 if (parentDir is Wix.Directory || parentDir is Wix.DirectoryRef)
774 {
775 directoryType = typeof(Wix.Directory);
776 directoryRefType = typeof(Wix.DirectoryRef);
777 }
778 else
779 {
780 throw new ArgumentException("GetSubDirElement parentDir");
781 }
782
783 // Search for an existing directory element.
784 foreach (Wix.ISchemaElement childElement in parentDir.Children)
785 {
786 if(VSProjectHarvester.AreTypesEquivalent(childElement.GetType(), directoryType))
787 {
788 DirectoryAttributeAccessor childDir = new DirectoryAttributeAccessor(childElement);
789 if (String.Equals(childDir.Name, firstSubDirName, StringComparison.OrdinalIgnoreCase))
790 {
791 subDir = childDir;
792 break;
793 }
794 }
795 }
796
797 if (subDir == null)
798 {
799 string parentId = null;
800 DirectoryAttributeAccessor parentDirectory = null;
801 DirectoryAttributeAccessor parentDirectoryRef = null;
802
803 if (VSProjectHarvester.AreTypesEquivalent(parentDir.GetType(), directoryType))
804 {
805 parentDirectory = new DirectoryAttributeAccessor((Wix.ISchemaElement)parentDir);
806 }
807 else if (VSProjectHarvester.AreTypesEquivalent(parentDir.GetType(), directoryRefType))
808 {
809 parentDirectoryRef = new DirectoryAttributeAccessor((Wix.ISchemaElement)parentDir);
810 }
811
812 if (parentDirectory != null)
813 {
814 parentId = parentDirectory.Id;
815 }
816 else if (parentDirectoryRef != null)
817 {
818 if (this.setUniqueIdentifiers)
819 {
820 //Use the GUID of the project instead of the project name to help keep things stable.
821 parentId = this.directoryRefSeed;
822 }
823 else
824 {
825 parentId = parentDirectoryRef.Id;
826 }
827 }
828
829 Wix.ISchemaElement newDirectory = (Wix.ISchemaElement)directoryType.GetConstructor(new Type[] { }).Invoke(null);
830 subDir = new DirectoryAttributeAccessor(newDirectory);
831
832 if (this.setUniqueIdentifiers)
833 {
834 subDir.Id = this.Core.GenerateIdentifier(DirectoryPrefix, parentId, firstSubDirName);
835 }
836 else
837 {
838 subDir.Id = String.Format(DirectoryIdFormat, parentId, firstSubDirName);
839 }
840
841 subDir.Name = firstSubDirName;
842
843 parentDir.AddChild(subDir.Element);
844 }
845
846 if (segments.Length == 1)
847 {
848 return subDir.ElementAsParent;
849 }
850 else
851 {
852 Uri nextRelativeUri = new Uri(Uri.UnescapeDataString(relativeUri.ToString()).Substring(firstSubDirName.Length + 1), UriKind.Relative);
853 return this.GetSubDirElement(subDir.ElementAsParent, nextRelativeUri);
854 }
855 }
856
857 private MSBuildProject GetMsbuildProject(string projectFile)
858 {
859 XmlDocument document = new XmlDocument();
860 try
861 {
862 document.Load(projectFile);
863 }
864 catch (Exception e)
865 {
866 throw new WixException(HarvesterErrors.CannotLoadProject(projectFile, e.Message));
867 }
868
869 string version = null;
870
871 if (this.UseToolsVersion)
872 {
873 foreach (XmlNode child in document.ChildNodes)
874 {
875 if (String.Equals(child.Name, "Project", StringComparison.Ordinal) && child.Attributes != null)
876 {
877 XmlNode toolsVersionAttribute = child.Attributes["ToolsVersion"];
878 if (toolsVersionAttribute != null)
879 {
880 version = toolsVersionAttribute.Value;
881 this.Core.Messaging.Write(HarvesterVerboses.FoundToolsVersion(version));
882
883 break;
884 }
885 }
886 }
887
888 switch (version)
889 {
890 case "4.0":
891 version = "4.0.0.0";
892 break;
893 case "12.0":
894 version = "12.0.0.0";
895 break;
896 case "14.0":
897 version = "14.0.0.0";
898 break;
899 default:
900 if (String.IsNullOrEmpty(this.MsbuildBinPath))
901 {
902 throw new WixException(HarvesterErrors.MsbuildBinPathRequired(version ?? "(none)"));
903 }
904
905 version = null;
906 break;
907 }
908 }
909
910 var project = this.ConstructMsbuild40Project(version);
911 return project;
912 }
913
914 private Assembly ResolveFromMsbuildBinPath(object sender, ResolveEventArgs args)
915 {
916 var assemblyName = new AssemblyName(args.Name);
917
918 var assemblyPath = Path.Combine(this.MsbuildBinPath, $"{assemblyName.Name}.dll");
919 if (!File.Exists(assemblyPath))
920 {
921 return null;
922 }
923
924 return Assembly.LoadFrom(assemblyPath);
925 }
926
927 private MSBuildProject ConstructMsbuild40Project(string loadVersion)
928 {
929 const string MSBuildEngineAssemblyName = "Microsoft.Build, Version={0}, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
930 const string MSBuildFrameworkAssemblyName = "Microsoft.Build.Framework, Version={0}, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
931 Assembly msbuildAssembly;
932 Assembly msbuildFrameworkAssembly;
933
934 if (loadVersion == null)
935 {
936 this.Core.Messaging.Write(HarvesterVerboses.LoadingProjectWithBinPath(this.MsbuildBinPath));
937 AppDomain.CurrentDomain.AssemblyResolve += this.ResolveFromMsbuildBinPath;
938
939 try
940 {
941 msbuildAssembly = Assembly.Load("Microsoft.Build");
942 }
943 catch (Exception e)
944 {
945 throw new WixException(HarvesterErrors.CannotLoadMSBuildAssembly(e.Message));
946 }
947
948 try
949 {
950 msbuildFrameworkAssembly = Assembly.Load("Microsoft.Build.Framework");
951 }
952 catch (Exception e)
953 {
954 throw new WixException(HarvesterErrors.CannotLoadMSBuildAssembly(e.Message));
955 }
956 }
957 else
958 {
959 this.Core.Messaging.Write(HarvesterVerboses.LoadingProjectWithVersion(loadVersion));
960
961 try
962 {
963 msbuildAssembly = Assembly.Load(String.Format(MSBuildEngineAssemblyName, loadVersion));
964 }
965 catch (Exception e)
966 {
967 throw new WixException(HarvesterErrors.CannotLoadMSBuildAssembly(e.Message));
968 }
969
970 try
971 {
972 msbuildFrameworkAssembly = Assembly.Load(String.Format(MSBuildFrameworkAssemblyName, loadVersion));
973 }
974 catch (Exception e)
975 {
976 throw new WixException(HarvesterErrors.CannotLoadMSBuildAssembly(e.Message));
977 }
978 }
979
980 Type projectType;
981 Type buildItemType;
982
983 Type buildManagerType;
984 Type buildParametersType;
985 Type buildRequestDataFlagsType;
986 Type buildRequestDataType;
987 Type hostServicesType;
988 Type projectCollectionType;
989 Type projectInstanceType;
990
991 Type writeHandlerType;
992 Type colorSetterType;
993 Type colorResetterType;
994 Type loggerVerbosityType;
995 Type consoleLoggerType;
996 Type iLoggerType;
997
998 try
999 {
1000 buildItemType = msbuildAssembly.GetType("Microsoft.Build.Execution.ProjectItemInstance", true);
1001 projectType = msbuildAssembly.GetType("Microsoft.Build.Evaluation.Project", true);
1002
1003 buildManagerType = msbuildAssembly.GetType("Microsoft.Build.Execution.BuildManager", true);
1004 buildParametersType = msbuildAssembly.GetType("Microsoft.Build.Execution.BuildParameters", true);
1005 buildRequestDataFlagsType = msbuildAssembly.GetType("Microsoft.Build.Execution.BuildRequestDataFlags", true);
1006 buildRequestDataType = msbuildAssembly.GetType("Microsoft.Build.Execution.BuildRequestData", true);
1007 hostServicesType = msbuildAssembly.GetType("Microsoft.Build.Execution.HostServices", true);
1008 projectCollectionType = msbuildAssembly.GetType("Microsoft.Build.Evaluation.ProjectCollection", true);
1009 projectInstanceType = msbuildAssembly.GetType("Microsoft.Build.Execution.ProjectInstance", true);
1010
1011 writeHandlerType = msbuildAssembly.GetType("Microsoft.Build.Logging.WriteHandler", true);
1012 colorSetterType = msbuildAssembly.GetType("Microsoft.Build.Logging.ColorSetter", true);
1013 colorResetterType = msbuildAssembly.GetType("Microsoft.Build.Logging.ColorResetter", true);
1014 loggerVerbosityType = msbuildFrameworkAssembly.GetType("Microsoft.Build.Framework.LoggerVerbosity", true);
1015 consoleLoggerType = msbuildAssembly.GetType("Microsoft.Build.Logging.ConsoleLogger", true);
1016 iLoggerType = msbuildFrameworkAssembly.GetType("Microsoft.Build.Framework.ILogger", true);
1017 }
1018 catch (TargetInvocationException tie)
1019 {
1020 throw new WixException(HarvesterErrors.CannotLoadMSBuildEngine(tie.InnerException.Message));
1021 }
1022 catch (Exception e)
1023 {
1024 throw new WixException(HarvesterErrors.CannotLoadMSBuildEngine(e.Message));
1025 }
1026
1027 MSBuild40Types types = new MSBuild40Types();
1028 types.buildManagerType = buildManagerType;
1029 types.buildParametersType = buildParametersType;
1030 types.buildRequestDataFlagsType = buildRequestDataFlagsType;
1031 types.buildRequestDataType = buildRequestDataType;
1032 types.hostServicesType = hostServicesType;
1033 types.projectCollectionType = projectCollectionType;
1034 types.projectInstanceType = projectInstanceType;
1035 types.writeHandlerType = writeHandlerType;
1036 types.colorSetterType = colorSetterType;
1037 types.colorResetterType = colorResetterType;
1038 types.loggerVerbosityType = loggerVerbosityType;
1039 types.consoleLoggerType = consoleLoggerType;
1040 types.iLoggerType = iLoggerType;
1041 return new MSBuild40Project(null, projectType, buildItemType, loadVersion, types, this.Core, this.configuration, this.platform);
1042 }
1043
1044 private static bool AreTypesEquivalent(Type a, Type b)
1045 {
1046 return (a == b) || (a.IsAssignableFrom(b) && b.IsAssignableFrom(a));
1047 }
1048
1049 private abstract class MSBuildProject
1050 {
1051 protected Type projectType;
1052 protected Type buildItemType;
1053 protected object project;
1054 private string loadVersion;
1055
1056 public MSBuildProject(object project, Type projectType, Type buildItemType, string loadVersion)
1057 {
1058 this.project = project;
1059 this.projectType = projectType;
1060 this.buildItemType = buildItemType;
1061 this.loadVersion = loadVersion;
1062 }
1063
1064 public string LoadVersion
1065 {
1066 get { return this.loadVersion; }
1067 }
1068
1069 public abstract bool Build(string projectFileName, string[] targetNames, IDictionary targetOutputs);
1070
1071 public abstract MSBuildProjectItemType GetBuildItem(object buildItem);
1072
1073 public abstract IEnumerable GetEvaluatedItemsByName(string itemName);
1074
1075 public abstract string GetEvaluatedProperty(string propertyName);
1076
1077 public abstract void Load(string projectFileName);
1078 }
1079
1080 private abstract class MSBuildProjectItemType
1081 {
1082 public MSBuildProjectItemType(object buildItem)
1083 {
1084 this.buildItem = buildItem;
1085 }
1086
1087 public abstract override string ToString();
1088
1089 public abstract string GetMetadata(string name);
1090
1091 protected object buildItem;
1092 }
1093
1094
1095 private struct MSBuild40Types
1096 {
1097 public Type buildManagerType;
1098 public Type buildParametersType;
1099 public Type buildRequestDataFlagsType;
1100 public Type buildRequestDataType;
1101 public Type hostServicesType;
1102 public Type projectCollectionType;
1103 public Type projectInstanceType;
1104 public Type writeHandlerType;
1105 public Type colorSetterType;
1106 public Type colorResetterType;
1107 public Type loggerVerbosityType;
1108 public Type consoleLoggerType;
1109 public Type iLoggerType;
1110 }
1111
1112 private class MSBuild40Project : MSBuildProject
1113 {
1114 private MSBuild40Types types;
1115 private object projectCollection;
1116 private object currentProjectInstance;
1117 private object buildManager;
1118 private object buildParameters;
1119 private IHarvesterCore harvesterCore;
1120
1121 public MSBuild40Project(object project, Type projectType, Type buildItemType, string loadVersion, MSBuild40Types types, IHarvesterCore harvesterCore, string configuration, string platform)
1122 : base(project, projectType, buildItemType, loadVersion)
1123 {
1124 this.types = types;
1125 this.harvesterCore = harvesterCore;
1126
1127 this.buildParameters = this.types.buildParametersType.GetConstructor(new Type[] { }).Invoke(null);
1128
1129 try
1130 {
1131 var loggers = this.CreateLoggers();
1132
1133 // this.buildParameters.Loggers = loggers;
1134 this.types.buildParametersType.GetProperty("Loggers").SetValue(this.buildParameters, loggers, null);
1135 }
1136 catch (TargetInvocationException tie)
1137 {
1138 if (this.harvesterCore != null)
1139 {
1140 this.harvesterCore.Messaging.Write(HarvesterWarnings.NoLogger(tie.InnerException.Message));
1141 }
1142 }
1143 catch (Exception e)
1144 {
1145 if (this.harvesterCore != null)
1146 {
1147 this.harvesterCore.Messaging.Write(HarvesterWarnings.NoLogger(e.Message));
1148 }
1149 }
1150
1151 this.buildManager = this.types.buildManagerType.GetConstructor(new Type[] { }).Invoke(null);
1152
1153 if (configuration != null || platform != null)
1154 {
1155 Dictionary<string, string> globalVariables = new Dictionary<string, string>();
1156 if (configuration != null)
1157 {
1158 globalVariables.Add("Configuration", configuration);
1159 }
1160
1161 if (platform != null)
1162 {
1163 globalVariables.Add("Platform", platform);
1164 }
1165
1166 this.projectCollection = this.types.projectCollectionType.GetConstructor(new Type[] { typeof(IDictionary<string, string>) }).Invoke(new object[] { globalVariables });
1167 }
1168 else
1169 {
1170 this.projectCollection = this.types.projectCollectionType.GetConstructor(new Type[] {}).Invoke(null);
1171 }
1172 }
1173
1174 private object CreateLoggers()
1175 {
1176 var logger = new HarvestLogger(this.harvesterCore.Messaging);
1177 var loggerVerbosity = Enum.Parse(this.types.loggerVerbosityType, "Minimal");
1178 var writeHandler = Delegate.CreateDelegate(this.types.writeHandlerType, logger, nameof(logger.LogMessage));
1179 var colorSetter = Delegate.CreateDelegate(this.types.colorSetterType, logger, nameof(logger.SetColor));
1180 var colorResetter = Delegate.CreateDelegate(this.types.colorResetterType, logger, nameof(logger.ResetColor));
1181
1182 var consoleLoggerCtor = this.types.consoleLoggerType.GetConstructor(new Type[] {
1183 this.types.loggerVerbosityType,
1184 this.types.writeHandlerType,
1185 this.types.colorSetterType,
1186 this.types.colorResetterType,
1187 });
1188 var consoleLogger = consoleLoggerCtor.Invoke(new object[] { loggerVerbosity, writeHandler, colorSetter, colorResetter });
1189
1190 var loggers = Array.CreateInstance(this.types.iLoggerType, 1);
1191 loggers.SetValue(consoleLogger, 0);
1192
1193 return loggers;
1194 }
1195
1196 public override bool Build(string projectFileName, string[] targetNames, IDictionary targetOutputs)
1197 {
1198 try
1199 {
1200 // this.buildManager.BeginBuild(this.buildParameters);
1201 this.types.buildManagerType.GetMethod("BeginBuild", new Type[] { this.types.buildParametersType }).Invoke(this.buildManager, new object[] { this.buildParameters });
1202
1203 // buildRequestData = new BuildRequestData(this.currentProjectInstance, targetNames, null, BuildRequestData.BuildRequestDataFlags.ReplaceExistingProjectInstance);
1204 ConstructorInfo buildRequestDataCtor = this.types.buildRequestDataType.GetConstructor(
1205 new Type[]
1206 {
1207 this.types.projectInstanceType, typeof(string[]), this.types.hostServicesType, this.types.buildRequestDataFlagsType
1208 });
1209 object buildRequestDataFlags = this.types.buildRequestDataFlagsType.GetField("ReplaceExistingProjectInstance").GetRawConstantValue();
1210 object buildRequestData = buildRequestDataCtor.Invoke(new object[] { this.currentProjectInstance, targetNames, null, buildRequestDataFlags });
1211
1212 // BuildSubmission submission = this.buildManager.PendBuildRequest(buildRequestData);
1213 object submission = this.types.buildManagerType.GetMethod("PendBuildRequest", new Type[] { this.types.buildRequestDataType })
1214 .Invoke(this.buildManager, new object[] { buildRequestData });
1215
1216 // BuildResult buildResult = submission.Execute();
1217 object buildResult = submission.GetType().GetMethod("Execute", new Type[] { }).Invoke(submission, null);
1218
1219 // bool buildSucceeded = buildResult.OverallResult == BuildResult.Success;
1220 object overallResult = buildResult.GetType().GetProperty("OverallResult").GetValue(buildResult, null);
1221 bool buildSucceeded = String.Equals(overallResult.ToString(), "Success", StringComparison.Ordinal);
1222
1223 // this.buildManager.EndBuild();
1224 this.types.buildManagerType.GetMethod("EndBuild", new Type[] { }).Invoke(this.buildManager, null);
1225
1226 // fill in empty lists for each target so that heat will look at the item group later
1227 foreach (string target in targetNames)
1228 {
1229 targetOutputs.Add(target, new List<object>());
1230 }
1231
1232 return buildSucceeded;
1233 }
1234 catch (TargetInvocationException tie)
1235 {
1236 throw new WixException(HarvesterErrors.CannotBuildProject(projectFileName, tie.InnerException.Message));
1237 }
1238 catch (Exception e)
1239 {
1240 throw new WixException(HarvesterErrors.CannotBuildProject(projectFileName, e.Message));
1241 }
1242 }
1243
1244 public override MSBuildProjectItemType GetBuildItem(object buildItem)
1245 {
1246 return new MSBuild40ProjectItemType(buildItem);
1247 }
1248
1249 public override IEnumerable GetEvaluatedItemsByName(string itemName)
1250 {
1251 MethodInfo getEvaluatedItem = this.types.projectInstanceType.GetMethod("GetItems", new Type[] { typeof(string) });
1252 return (IEnumerable)getEvaluatedItem.Invoke(this.currentProjectInstance, new object[] { itemName });
1253 }
1254
1255 public override string GetEvaluatedProperty(string propertyName)
1256 {
1257 MethodInfo getProperty = this.types.projectInstanceType.GetMethod("GetPropertyValue", new Type[] { typeof(string) });
1258 return (string)getProperty.Invoke(this.currentProjectInstance, new object[] { propertyName });
1259 }
1260
1261 public override void Load(string projectFileName)
1262 {
1263 try
1264 {
1265 //this.project = this.projectCollection.LoadProject(projectFileName);
1266 this.project = this.types.projectCollectionType.GetMethod("LoadProject", new Type[] { typeof(string) }).Invoke(this.projectCollection, new object[] { projectFileName });
1267
1268 // this.currentProjectInstance = this.project.CreateProjectInstance();
1269 MethodInfo createProjectInstanceMethod = this.projectType.GetMethod("CreateProjectInstance", new Type[] { });
1270 this.currentProjectInstance = createProjectInstanceMethod.Invoke(this.project, null);
1271 }
1272 catch (TargetInvocationException tie)
1273 {
1274 throw new WixException(HarvesterErrors.CannotLoadProject(projectFileName, tie.InnerException.Message));
1275 }
1276 catch (Exception e)
1277 {
1278 throw new WixException(HarvesterErrors.CannotLoadProject(projectFileName, e.Message));
1279 }
1280 }
1281 }
1282
1283 private class MSBuild40ProjectItemType : MSBuildProjectItemType
1284 {
1285 public MSBuild40ProjectItemType(object buildItem)
1286 : base(buildItem)
1287 {
1288 }
1289
1290 public override string ToString()
1291 {
1292 PropertyInfo includeProperty = this.buildItem.GetType().GetProperty("EvaluatedInclude");
1293 return (string)includeProperty.GetValue(this.buildItem, null);
1294 }
1295
1296 public override string GetMetadata(string name)
1297 {
1298 MethodInfo getMetadataMethod = this.buildItem.GetType().GetMethod("GetMetadataValue");
1299 if (null != getMetadataMethod)
1300 {
1301 return (string)getMetadataMethod.Invoke(this.buildItem, new object[] { name });
1302 }
1303 return string.Empty;
1304 }
1305 }
1306
1307 /// <summary>
1308 /// Used internally in the VSProjectHarvester class to encapsulate
1309 /// the settings for a particular MSBuild "project output group".
1310 /// </summary>
1311 private struct ProjectOutputGroup
1312 {
1313 public readonly string Name;
1314 public readonly string BuildOutputGroup;
1315 public readonly string FileSource;
1316
1317 /// <summary>
1318 /// Creates a new project output group.
1319 /// </summary>
1320 /// <param name="name">Friendly name used by heat.</param>
1321 /// <param name="buildOutputGroup">MSBuild's name of the project output group.</param>
1322 /// <param name="fileSource">VS directory token containing the files of the POG.</param>
1323 public ProjectOutputGroup(string name, string buildOutputGroup, string fileSource)
1324 {
1325 this.Name = name;
1326 this.BuildOutputGroup = buildOutputGroup;
1327 this.FileSource = fileSource;
1328 }
1329 }
1330
1331 /// <summary>
1332 /// Internal class for getting and setting common attrbiutes on
1333 /// directory elements.
1334 /// </summary>
1335 internal class DirectoryAttributeAccessor
1336 {
1337 public Wix.ISchemaElement directoryElement;
1338
1339 public DirectoryAttributeAccessor(Wix.ISchemaElement directoryElement)
1340 {
1341 this.directoryElement = directoryElement;
1342 }
1343
1344 /// <summary>
1345 /// Gets the element as a ISchemaElement.
1346 /// </summary>
1347 public Wix.ISchemaElement Element
1348 {
1349 get { return this.directoryElement; }
1350 }
1351
1352 /// <summary>
1353 /// Gets the element as a IParentElement.
1354 /// </summary>
1355 public Wix.IParentElement ElementAsParent
1356 {
1357 get { return (Wix.IParentElement)this.directoryElement; }
1358 }
1359
1360 /// <summary>
1361 /// Gets or sets the Id attrbiute.
1362 /// </summary>
1363 public string Id
1364 {
1365 get
1366 {
1367 if (this.directoryElement is Wix.Directory wixDirectory)
1368 {
1369 return wixDirectory.Id;
1370 }
1371 else if (this.directoryElement is Wix.DirectoryRef wixDirectoryRef)
1372 {
1373 return wixDirectoryRef.Id;
1374 }
1375 else
1376 {
1377 throw new WixException(HarvesterErrors.DirectoryAttributeAccessorBadType("Id"));
1378 }
1379 }
1380 set
1381 {
1382 if (this.directoryElement is Wix.Directory wixDirectory)
1383 {
1384 wixDirectory.Id = value;
1385 }
1386 else if (this.directoryElement is Wix.DirectoryRef wixDirectoryRef)
1387 {
1388 wixDirectoryRef.Id = value;
1389 }
1390 else
1391 {
1392 throw new WixException(HarvesterErrors.DirectoryAttributeAccessorBadType("Id"));
1393 }
1394 }
1395 }
1396
1397 /// <summary>
1398 /// Gets or sets the Name attribute.
1399 /// </summary>
1400 public string Name
1401 {
1402 get
1403 {
1404 if (this.directoryElement is Wix.Directory wixDirectory)
1405 {
1406 return wixDirectory.Name;
1407 }
1408 else
1409 {
1410 throw new WixException(HarvesterErrors.DirectoryAttributeAccessorBadType("Name"));
1411 }
1412 }
1413 set
1414 {
1415 if (this.directoryElement is Wix.Directory wixDirectory)
1416 {
1417 wixDirectory.Name = value;
1418 }
1419 else
1420 {
1421 throw new WixException(HarvesterErrors.DirectoryAttributeAccessorBadType("Name"));
1422 }
1423 }
1424 }
1425 }
1426
1427 internal class HarvestLogger
1428 {
1429 public HarvestLogger(IMessaging messaging)
1430 {
1431 this.Color = ConsoleColor.Black;
1432 this.Messaging = messaging;
1433 }
1434
1435 private ConsoleColor Color { get; set; }
1436 private IMessaging Messaging { get; }
1437
1438 public void LogMessage(string message)
1439 {
1440 if (this.Color == ConsoleColor.Red)
1441 {
1442 this.Messaging.Write(HarvesterErrors.BuildErrorDuringHarvesting(message));
1443 }
1444 }
1445
1446 public void SetColor(ConsoleColor color)
1447 {
1448 this.Color = color;
1449 }
1450
1451 public void ResetColor()
1452 {
1453 this.Color = ConsoleColor.Black;
1454 }
1455 }
1456 }
1457}