main
cs 182 lines 7.61 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 namespace WixToolset.BuildTasks
4 {
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using Microsoft.Build.Framework;
9 using Microsoft.Build.Utilities;
10 using WixToolset.Dtf.WindowsInstaller;
11
12 /// <summary>
13 /// This task assigns Culture metadata to files based on the value of the Culture attribute on the
14 /// WixLocalization element inside the file.
15 /// </summary>
16 public class GetLooseFileList : Task
17 {
18 internal const int MsidbFileAttributesNoncompressed = 8192;
19 internal const int MsidbFileAttributesCompressed = 16384;
20
21 /// <summary>
22 /// The list of database files to find Loose Files in
23 /// </summary>
24 [Required]
25 public ITaskItem Database { get; set; }
26
27 /// <summary>
28 /// The total list of Loose Files in this database
29 /// </summary>
30 [Output]
31 public ITaskItem[] LooseFileList { get; private set; }
32
33 /// <summary>
34 /// Gets a complete list of external Loose Files referenced by the given installer database file.
35 /// </summary>
36 /// <returns>True upon completion of the task execution.</returns>
37 public override bool Execute()
38 {
39 var databaseFile = this.Database.ItemSpec;
40 var looseFileNames = new List<ITaskItem>();
41 var ComponentFullDirectory = new Dictionary<string, string>();
42 var DirectoryIdDefaultDir = new Dictionary<string, string>();
43 var DirectoryIdParent = new Dictionary<string, string>();
44 var DirectoryIdFullSource = new Dictionary<string, string>();
45
46 // If the file doesn't exist, no Loose Files to return, so exit now
47 if (!File.Exists(databaseFile))
48 {
49 return true;
50 }
51
52 var databaseDir = Path.GetDirectoryName(databaseFile);
53
54 using (var database = new Database(databaseFile))
55 {
56 // If the File table doesn't exist, no Loose Files to return, so exit now
57 if (null == database.Tables["File"])
58 {
59 return true;
60 }
61
62 var compressed = 2 == (database.SummaryInfo.WordCount & 2);
63
64 // Only setup all these helpful indexes if the database is marked as uncompressed. If it's marked as compressed, files are stored at the root,
65 // so none of these indexes will be used
66 if (!compressed)
67 {
68 if (null == database.Tables["Directory"] || null == database.Tables["Component"])
69 {
70 return true;
71 }
72
73 var directoryRecords = database.ExecuteQuery("SELECT `Directory`,`Directory_Parent`,`DefaultDir` FROM `Directory`");
74
75 // First setup a simple index from DirectoryId to DefaultDir
76 for (var i = 0; i < directoryRecords.Count; i += 3)
77 {
78 var directoryId = (string)directoryRecords[i];
79 var directoryParent = (string)directoryRecords[i + 1];
80 var defaultDir = (string)directoryRecords[i + 2];
81
82 var sourceDir = this.SourceDirFromDefaultDir(defaultDir);
83
84 DirectoryIdDefaultDir[directoryId] = sourceDir;
85 DirectoryIdParent[directoryId] = directoryParent;
86 }
87
88 // Setup an index from directory Id to the full source path
89 for (var i = 0; i < directoryRecords.Count; i += 3)
90 {
91 var directoryId = (string)directoryRecords[i];
92 var directoryParent = (string)directoryRecords[i + 1];
93
94 var sourceDir = DirectoryIdDefaultDir[directoryId];
95
96 // The TARGETDIR case
97 if (String.IsNullOrEmpty(directoryParent))
98 {
99 DirectoryIdFullSource[directoryId] = databaseDir;
100 }
101 else
102 {
103 var tempDirectoryParent = directoryParent;
104
105 while (!String.IsNullOrEmpty(tempDirectoryParent) && !String.IsNullOrEmpty(DirectoryIdParent[tempDirectoryParent]))
106 {
107 sourceDir = Path.Combine(DirectoryIdDefaultDir[tempDirectoryParent], sourceDir);
108
109 tempDirectoryParent = DirectoryIdParent[tempDirectoryParent];
110 }
111
112 DirectoryIdFullSource[directoryId] = Path.Combine(databaseDir, sourceDir);
113 }
114 }
115
116 // Setup an index from component Id to full directory path
117 var componentRecords = database.ExecuteQuery("SELECT `Component`,`Directory_` FROM `Component`");
118
119 for (var i = 0; i < componentRecords.Count; i += 2)
120 {
121 var componentId = (string)componentRecords[i];
122 var componentDir = (string)componentRecords[i + 1];
123
124 ComponentFullDirectory[componentId] = DirectoryIdFullSource[componentDir];
125 }
126 }
127
128 var fileRecords = database.ExecuteQuery("SELECT `Component_`,`FileName`,`Attributes` FROM `File`");
129
130 for (var i = 0; i < fileRecords.Count; i += 3)
131 {
132 var componentId = (string)fileRecords[i];
133 var fileName = this.SourceFileFromFileName((string)fileRecords[i + 1]);
134 var attributes = (int)fileRecords[i + 2];
135
136 // If the whole database is marked uncompressed, use the directory layout made above
137 if (!compressed && MsidbFileAttributesCompressed != (attributes & MsidbFileAttributesCompressed))
138 {
139 looseFileNames.Add(new TaskItem(Path.GetFullPath(Path.Combine(ComponentFullDirectory[componentId], fileName))));
140 }
141 // If the database is marked as compressed, put files at the root
142 else if (compressed && (MsidbFileAttributesNoncompressed == (attributes & MsidbFileAttributesNoncompressed)))
143 {
144 looseFileNames.Add(new TaskItem(Path.GetFullPath(Path.Combine(databaseDir, fileName))));
145 }
146 }
147 }
148
149 this.LooseFileList = looseFileNames.ToArray();
150
151 return true;
152 }
153
154 /// <summary>
155 /// Takes the "defaultDir" column
156 /// </summary>
157 /// <returns>Returns the corresponding sourceDir.</returns>
158 public string SourceDirFromDefaultDir(string defaultDir)
159 {
160 var split = defaultDir.Split(':');
161
162 var sourceDir = (1 == split.Length) ? split[0] : split[1];
163
164 split = sourceDir.Split('|');
165
166 sourceDir = (1 == split.Length) ? split[0] : split[1];
167
168 return sourceDir;
169 }
170
171 /// <summary>
172 /// Takes the "FileName" column
173 /// </summary>
174 /// <returns>Returns the corresponding source file name.</returns>
175 private string SourceFileFromFileName(string fileName)
176 {
177 var split = fileName.Split('|');
178
179 return (1 == split.Length) ? split[0] : split[1];
180 }
181 }
182 }