1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
// 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.
namespace WixToolset.BuildTasks
{
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
/// <summary>
/// This task generates metadata on the for compile output objects.
/// </summary>
public class GenerateCompileWithObjectPath : Task
{
/// <summary>
/// The list of files to generate outputs for.
/// </summary>
[Required]
public ITaskItem[] Compile
{
get;
set;
}
/// <summary>
/// The list of files with ObjectPath metadata.
/// </summary>
[Output]
public ITaskItem[] CompileWithObjectPath
{
get;
private set;
}
/// <summary>
/// The folder under which all ObjectPaths should reside.
/// </summary>
[Required]
public string IntermediateOutputPath
{
get;
set;
}
/// <summary>
/// Generate an identifier by hashing data from the row.
/// </summary>
/// <param name="prefix">Three letter or less prefix for generated row identifier.</param>
/// <param name="args">Information to hash.</param>
/// <returns>The generated identifier.</returns>
[SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.InvalidOperationException.#ctor(System.String)")]
public static string GenerateIdentifier(string prefix, params string[] args)
{
string stringData = String.Join("|", args);
byte[] data = Encoding.Unicode.GetBytes(stringData);
// hash the data
byte[] hash;
using (MD5 md5 = new MD5CryptoServiceProvider())
{
hash = md5.ComputeHash(data);
}
// build up the identifier
StringBuilder identifier = new StringBuilder(35, 35);
identifier.Append(prefix);
// hard coded to 16 as that is the most bytes that can be used to meet the length requirements. SHA1 is 20 bytes.
for (int i = 0; i < 16; i++)
{
identifier.Append(hash[i].ToString("X2", CultureInfo.InvariantCulture.NumberFormat));
}
return identifier.ToString();
}
/// <summary>
/// Gets the full path of the directory in which the file is found.
/// </summary>
/// <param name='file'>The file from which to extract the directory.</param>
/// <returns>The generated identifier.</returns>
private static string GetDirectory(ITaskItem file)
{
return file.GetMetadata("RootDir") + file.GetMetadata("Directory");
}
/// <summary>
/// Sets the object path to use for the file.
/// </summary>
/// <param name='file'>The file on which to set the ObjectPath metadata.</param>
/// <remarks>
/// For the same input path it will return the same ObjectPath. Case is not ignored, however that isn't a problem.
/// </remarks>
private void SetObjectPath(ITaskItem file)
{
// If the source file is in the project directory or in the intermediate directory, use the intermediate directory.
if (string.IsNullOrEmpty(file.GetMetadata("RelativeDir")) || string.Compare(file.GetMetadata("RelativeDir"), this.IntermediateOutputPath, StringComparison.OrdinalIgnoreCase) == 0)
{
file.SetMetadata("ObjectPath", this.IntermediateOutputPath);
}
// Otherwise use a subdirectory of the intermediate directory. The subfolder's name is based on the full path of the folder containing the source file.
else
{
file.SetMetadata("ObjectPath", Path.Combine(this.IntermediateOutputPath, GenerateIdentifier("pth", GetDirectory(file))) + Path.DirectorySeparatorChar);
}
}
/// <summary>
/// Gets a complete list of external cabs referenced by the given installer database file.
/// </summary>
/// <returns>True upon completion of the task execution.</returns>
public override bool Execute()
{
if (string.IsNullOrEmpty(this.IntermediateOutputPath))
{
this.Log.LogError("IntermediateOutputPath parameter is required and cannot be empty");
return false;
}
if (this.Compile == null || this.Compile.Length == 0)
{
return true;
}
this.CompileWithObjectPath = new ITaskItem[this.Compile.Length];
for (int i = 0; i < this.Compile.Length; ++i)
{
this.CompileWithObjectPath[i] = new TaskItem(this.Compile[i].ItemSpec, this.Compile[i].CloneCustomMetadata());
// Do not overwrite the ObjectPath metadata if it already was set.
if (string.IsNullOrEmpty(this.CompileWithObjectPath[i].GetMetadata("ObjectPath")))
{
SetObjectPath(this.CompileWithObjectPath[i]);
}
}
return true;
}
}
}
|