aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.BuildTasks/CreateProjectReferenceDefineConstants.cs
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2017-09-17 15:35:20 -0700
committerRob Mensching <rob@firegiant.com>2017-09-17 16:00:11 -0700
commitd3d3649a68cb1fa589fdd987a6690dbd5d671f0d (patch)
tree44fe37ee352b7e3a355cc1e08b1d7d5988c14cc0 /src/WixToolset.BuildTasks/CreateProjectReferenceDefineConstants.cs
parenta62610d23d6feb98be3b1e529a4e81b19d77d9d8 (diff)
downloadwix-d3d3649a68cb1fa589fdd987a6690dbd5d671f0d.tar.gz
wix-d3d3649a68cb1fa589fdd987a6690dbd5d671f0d.tar.bz2
wix-d3d3649a68cb1fa589fdd987a6690dbd5d671f0d.zip
Initial code commit
Diffstat (limited to 'src/WixToolset.BuildTasks/CreateProjectReferenceDefineConstants.cs')
-rw-r--r--src/WixToolset.BuildTasks/CreateProjectReferenceDefineConstants.cs270
1 files changed, 270 insertions, 0 deletions
diff --git a/src/WixToolset.BuildTasks/CreateProjectReferenceDefineConstants.cs b/src/WixToolset.BuildTasks/CreateProjectReferenceDefineConstants.cs
new file mode 100644
index 00000000..5eae0850
--- /dev/null
+++ b/src/WixToolset.BuildTasks/CreateProjectReferenceDefineConstants.cs
@@ -0,0 +1,270 @@
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.BuildTasks
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Globalization;
8 using System.IO;
9 using Microsoft.Build.Framework;
10 using Microsoft.Build.Utilities;
11
12 /// <summary>
13 /// An MSBuild task to create a list of preprocessor defines to be passed to candle from the
14 /// list of referenced projects.
15 /// </summary>
16 public sealed class CreateProjectReferenceDefineConstants : Task
17 {
18 private ITaskItem[] defineConstants;
19 private ITaskItem[] projectConfigurations;
20 private ITaskItem[] projectReferencePaths;
21
22 [Output]
23 public ITaskItem[] DefineConstants
24 {
25 get { return this.defineConstants; }
26 }
27
28 [Required]
29 public ITaskItem[] ProjectReferencePaths
30 {
31 get { return this.projectReferencePaths; }
32 set { this.projectReferencePaths = value; }
33 }
34
35 public ITaskItem[] ProjectConfigurations
36 {
37 get { return this.projectConfigurations; }
38 set { this.projectConfigurations = value; }
39 }
40
41 public override bool Execute()
42 {
43 List<ITaskItem> outputItems = new List<ITaskItem>();
44 Dictionary<string, string> defineConstants = new Dictionary<string, string>();
45
46 for (int i = 0; i < this.ProjectReferencePaths.Length; i++)
47 {
48 ITaskItem item = this.ProjectReferencePaths[i];
49
50 string configuration = item.GetMetadata("Configuration");
51 string fullConfiguration = item.GetMetadata("FullConfiguration");
52 string platform = item.GetMetadata("Platform");
53
54 string projectPath = CreateProjectReferenceDefineConstants.GetProjectPath(this.ProjectReferencePaths, i);
55 string projectDir = Path.GetDirectoryName(projectPath) + Path.DirectorySeparatorChar;
56 string projectExt = Path.GetExtension(projectPath);
57 string projectFileName = Path.GetFileName(projectPath);
58 string projectName = Path.GetFileNameWithoutExtension(projectPath);
59
60 string referenceName = CreateProjectReferenceDefineConstants.GetReferenceName(item, projectName);
61
62 string targetPath = item.GetMetadata("FullPath");
63 string targetDir = Path.GetDirectoryName(targetPath) + Path.DirectorySeparatorChar;
64 string targetExt = Path.GetExtension(targetPath);
65 string targetFileName = Path.GetFileName(targetPath);
66 string targetName = Path.GetFileNameWithoutExtension(targetPath);
67
68 // If there is no configuration metadata on the project reference task item,
69 // check for any additional configuration data provided in the optional task property.
70 if (String.IsNullOrEmpty(fullConfiguration))
71 {
72 fullConfiguration = this.FindProjectConfiguration(projectName);
73 if (!String.IsNullOrEmpty(fullConfiguration))
74 {
75 string[] typeAndPlatform = fullConfiguration.Split('|');
76 configuration = typeAndPlatform[0];
77 platform = (typeAndPlatform.Length > 1 ? typeAndPlatform[1] : String.Empty);
78 }
79 }
80
81 // write out the platform/configuration defines
82 defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.Configuration", referenceName)] = configuration;
83 defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.FullConfiguration", referenceName)] = fullConfiguration;
84 defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.Platform", referenceName)] = platform;
85
86 // write out the ProjectX defines
87 defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.ProjectDir", referenceName)] = projectDir;
88 defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.ProjectExt", referenceName)] = projectExt;
89 defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.ProjectFileName", referenceName)] = projectFileName;
90 defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.ProjectName", referenceName)] = projectName;
91 defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.ProjectPath", referenceName)] = projectPath;
92
93 // write out the TargetX defines
94 string targetDirDefine = String.Format(CultureInfo.InvariantCulture, "{0}.TargetDir", referenceName);
95 if (defineConstants.ContainsKey(targetDirDefine))
96 {
97 //if target dir was already defined, redefine it as the common root shared by multiple references from the same project
98 string commonDir = FindCommonRoot(targetDir, defineConstants[targetDirDefine]);
99 if (!String.IsNullOrEmpty(commonDir))
100 {
101 targetDir = commonDir;
102 }
103 }
104 defineConstants[targetDirDefine] = CreateProjectReferenceDefineConstants.EnsureEndsWithBackslash(targetDir);
105
106 defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.TargetExt", referenceName)] = targetExt;
107 defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.TargetFileName", referenceName)] = targetFileName;
108 defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.TargetName", referenceName)] = targetName;
109
110 //if target path was already defined, append to it creating a list of multiple references from the same project
111 string targetPathDefine = String.Format(CultureInfo.InvariantCulture, "{0}.TargetPath", referenceName);
112 if (defineConstants.ContainsKey(targetPathDefine))
113 {
114 string oldTargetPath = defineConstants[targetPathDefine];
115 if (!targetPath.Equals(oldTargetPath, StringComparison.OrdinalIgnoreCase))
116 {
117 defineConstants[targetPathDefine] += ";" + targetPath;
118 }
119
120 //If there was only one targetpath we need to create its culture specific define
121 if (!oldTargetPath.Contains(";"))
122 {
123 string oldSubFolder = FindSubfolder(oldTargetPath, targetDir, targetFileName);
124 if (!String.IsNullOrEmpty(oldSubFolder))
125 {
126 defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.{1}.TargetPath", referenceName, oldSubFolder.Replace('\\', '_'))] = oldTargetPath;
127 }
128 }
129
130 // Create a culture specific define
131 string subFolder = FindSubfolder(targetPath, targetDir, targetFileName);
132 if (!String.IsNullOrEmpty(subFolder))
133 {
134 defineConstants[String.Format(CultureInfo.InvariantCulture, "{0}.{1}.TargetPath", referenceName, subFolder.Replace('\\', '_'))] = targetPath;
135 }
136
137 }
138 else
139 {
140 defineConstants[targetPathDefine] = targetPath;
141 }
142 }
143
144 foreach (KeyValuePair<string, string> define in defineConstants)
145 {
146 outputItems.Add(new TaskItem(String.Format(CultureInfo.InvariantCulture, "{0}={1}", define.Key, define.Value)));
147 }
148 this.defineConstants = outputItems.ToArray();
149
150 return true;
151 }
152
153 public static string GetProjectPath(ITaskItem[] projectReferencePaths, int i)
154 {
155 return projectReferencePaths[i].GetMetadata("MSBuildSourceProjectFile");
156 }
157
158 public static string GetReferenceName(ITaskItem item, string projectName)
159 {
160 string referenceName = item.GetMetadata("Name");
161 if (String.IsNullOrEmpty(referenceName))
162 {
163 referenceName = projectName;
164 }
165
166 // We cannot have an equals sign in the variable name because it
167 // messes with the preprocessor definitions on the command line.
168 referenceName = referenceName.Replace('=', '_');
169
170 // We cannot have a double quote on the command line because it
171 // there is no way to escape it on the command line.
172 referenceName = referenceName.Replace('\"', '_');
173
174 // We cannot have parens in the variable name because the WiX
175 // preprocessor will not be able to parse it.
176 referenceName = referenceName.Replace('(', '_');
177 referenceName = referenceName.Replace(')', '_');
178
179 return referenceName;
180 }
181
182 /// <summary>
183 /// Look through the configuration data in the ProjectConfigurations property
184 /// to find the configuration for a project, if available.
185 /// </summary>
186 /// <param name="projectName">Name of the project that is being searched for.</param>
187 /// <returns>Full configuration spec, for example "Release|Win32".</returns>
188 private string FindProjectConfiguration(string projectName)
189 {
190 string configuration = String.Empty;
191
192 if (this.ProjectConfigurations != null)
193 {
194 foreach (ITaskItem configItem in this.ProjectConfigurations)
195 {
196 string configProject = configItem.ItemSpec;
197 if (configProject.Length > projectName.Length &&
198 configProject.StartsWith(projectName) &&
199 configProject[projectName.Length] == '=')
200 {
201 configuration = configProject.Substring(projectName.Length + 1);
202 break;
203 }
204 }
205 }
206
207 return configuration;
208 }
209
210 /// <summary>
211 /// Finds the common root between two paths
212 /// </summary>
213 /// <param name="path1"></param>
214 /// <param name="path2"></param>
215 /// <returns>common root on success, empty string on failure</returns>
216 private static string FindCommonRoot(string path1, string path2)
217 {
218 path1 = path1.TrimEnd(Path.DirectorySeparatorChar);
219 path2 = path2.TrimEnd(Path.DirectorySeparatorChar);
220
221 while (!String.IsNullOrEmpty(path1))
222 {
223 for (string searchPath = path2; !String.IsNullOrEmpty(searchPath); searchPath = Path.GetDirectoryName(searchPath))
224 {
225 if (path1.Equals(searchPath, StringComparison.OrdinalIgnoreCase))
226 {
227 return searchPath;
228 }
229 }
230
231 path1 = Path.GetDirectoryName(path1);
232 }
233
234 return path1;
235 }
236
237 /// <summary>
238 /// Finds the subfolder of a path, excluding a root and filename.
239 /// </summary>
240 /// <param name="path">Path to examine</param>
241 /// <param name="rootPath">Root that must be present </param>
242 /// <param name="fileName"></param>
243 /// <returns></returns>
244 private static string FindSubfolder(string path, string rootPath, string fileName)
245 {
246 if (Path.GetFileName(path).Equals(fileName, StringComparison.OrdinalIgnoreCase))
247 {
248 path = Path.GetDirectoryName(path);
249 }
250
251 if (path.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase))
252 {
253 // cut out the root and return the subpath
254 return path.Substring(rootPath.Length).Trim(Path.DirectorySeparatorChar);
255 }
256
257 return String.Empty;
258 }
259
260 private static string EnsureEndsWithBackslash(string dir)
261 {
262 if (dir[dir.Length - 1] != Path.DirectorySeparatorChar)
263 {
264 dir += Path.DirectorySeparatorChar;
265 }
266
267 return dir;
268 }
269 }
270}