1 // Copyright (c) Microsoft. All rights reserved.
2 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 //-----------------------------------------------------------------------
4 // </copyright>
5 // <summary>Class containing data for a build request.</summary>
6 //-----------------------------------------------------------------------
7 
8 using System;
9 using System.Collections.Generic;
10 using System.Diagnostics;
11 using System.Text;
12 using Microsoft.Build.Shared;
13 using Microsoft.Build.Framework;
14 using Microsoft.Build.Execution;
15 
16 using Constants = Microsoft.Build.Internal.Constants;
17 
18 namespace Microsoft.Build.BackEnd
19 {
20     /// <summary>
21     /// A build request contains information about the configuration used to build as well
22     /// as which targets need to be built.
23     /// </summary>
24     internal class BuildRequest : INodePacket
25     {
26         /// <summary>
27         /// The invalid global request id
28         /// </summary>
29         public const int InvalidGlobalRequestId = -1;
30 
31         /// <summary>
32         /// The invalid node request id
33         /// </summary>
34         public const int InvalidNodeRequestId = 0;
35 
36         /// <summary>
37         /// The results transfer request id
38         /// </summary>
39         public const int ResultsTransferNodeRequestId = -1;
40 
41         /// <summary>
42         /// The submission with which this request is associated.
43         /// </summary>
44         private int _submissionId;
45 
46         /// <summary>
47         /// The configuration id.
48         /// </summary>
49         private int _configurationId;
50 
51         /// <summary>
52         /// The global build request id, assigned by the Build Manager
53         /// </summary>
54         private int _globalRequestId;
55 
56         /// <summary>
57         /// The global request id of the request which spawned this one.
58         /// </summary>
59         private int _parentGlobalRequestId;
60 
61         /// <summary>
62         /// The build request id assigned by the node originating this request.
63         /// </summary>
64         private int _nodeRequestId;
65 
66         /// <summary>
67         /// The targets specified when the request was made.  Doesn't include default or initial targets.
68         /// </summary>
69         private List<string> _targets;
70 
71         /// <summary>
72         /// The route for host-aware tasks back to the host
73         /// </summary>
74         private HostServices _hostServices;
75 
76         /// <summary>
77         /// The build event context of the parent
78         /// </summary>
79         private BuildEventContext _parentBuildEventContext;
80 
81         /// <summary>
82         /// The build event context of this request
83         /// </summary>
84         private BuildEventContext _buildEventContext;
85 
86         /// <summary>
87         /// Whether or not the <see cref="BuildResult"/> issued in response to this request should include <see cref="BuildResult.ProjectStateAfterBuild"/>.
88         /// </summary>
89         private BuildRequestDataFlags _buildRequestDataFlags;
90 
91         /// <summary>
92         /// Filter describing properties, items, and metadata of interest for this request.
93         /// </summary>
94         private RequestedProjectState _requestedProjectState;
95 
96         /// <summary>
97         /// If set, skip targets that are not defined in the projects to be built.
98         /// </summary>
99         private bool _skipNonexistentTargets;
100 
101         /// <summary>
102         /// Constructor for serialization.
103         /// </summary>
BuildRequest()104         public BuildRequest()
105         {
106         }
107 
108         /// <summary>
109         /// Initializes a build request with a parent context.
110         /// </summary>
111         /// <param name="submissionId">The id of the build submission.</param>
112         /// <param name="nodeRequestId">The id of the node issuing the request</param>
113         /// <param name="configurationId">The configuration id to use.</param>
114         /// <param name="escapedTargets">The targets to be built</param>
115         /// <param name="hostServices">Host services if any. May be null.</param>
116         /// <param name="parentBuildEventContext">The build event context of the parent project.</param>
117         /// <param name="parentRequest">The parent build request, if any.</param>
118         /// <param name="buildRequestDataFlags">Additional flags for the request.</param>
119         /// <param name="requestedProjectState">Filter for desired build results.</param>
BuildRequest( int submissionId, int nodeRequestId, int configurationId, ICollection<string> escapedTargets, HostServices hostServices, BuildEventContext parentBuildEventContext, BuildRequest parentRequest, BuildRequestDataFlags buildRequestDataFlags = BuildRequestDataFlags.None, RequestedProjectState requestedProjectState = null)120         public BuildRequest(
121             int submissionId,
122             int nodeRequestId,
123             int configurationId,
124             ICollection<string> escapedTargets,
125             HostServices hostServices,
126             BuildEventContext parentBuildEventContext,
127             BuildRequest parentRequest,
128             BuildRequestDataFlags buildRequestDataFlags = BuildRequestDataFlags.None,
129             RequestedProjectState requestedProjectState = null)
130         {
131             ErrorUtilities.VerifyThrowArgumentNull(escapedTargets, "targets");
132             ErrorUtilities.VerifyThrowArgumentNull(parentBuildEventContext, "parentBuildEventContext");
133 
134             _submissionId = submissionId;
135             _configurationId = configurationId;
136 
137             // When targets come into a build request, we unescape them.
138             _targets = new List<string>(escapedTargets.Count);
139             foreach (string target in escapedTargets)
140             {
141                 _targets.Add(EscapingUtilities.UnescapeAll(target));
142             }
143 
144             _hostServices = hostServices;
145             _buildEventContext = BuildEventContext.Invalid;
146             _parentBuildEventContext = parentBuildEventContext;
147             _globalRequestId = InvalidGlobalRequestId;
148             _parentGlobalRequestId = parentRequest?.GlobalRequestId ?? InvalidGlobalRequestId;
149 
150             _nodeRequestId = nodeRequestId;
151             _buildRequestDataFlags = buildRequestDataFlags;
152             _requestedProjectState = requestedProjectState;
153         }
154 
155         /// <summary>
156         /// Private constructor for deserialization
157         /// </summary>
BuildRequest(INodePacketTranslator translator)158         private BuildRequest(INodePacketTranslator translator)
159         {
160             Translate(translator);
161         }
162 
163         /// <summary>
164         /// Returns true if the configuration has been resolved, false otherwise.
165         /// </summary>
166         public bool IsConfigurationResolved
167         {
168             [DebuggerStepThrough]
169             get
170             { return _configurationId > 0; }
171         }
172 
173         /// <summary>
174         /// Returns the submission id
175         /// </summary>
176         public int SubmissionId
177         {
178             [DebuggerStepThrough]
179             get
180             { return _submissionId; }
181         }
182 
183         /// <summary>
184         /// Returns the configuration id
185         /// </summary>
186         public int ConfigurationId
187         {
188             [DebuggerStepThrough]
189             get
190             { return _configurationId; }
191         }
192 
193         /// <summary>
194         /// Gets the global request id
195         /// </summary>
196         public int GlobalRequestId
197         {
198             [DebuggerStepThrough]
199             get
200             {
201                 return _globalRequestId;
202             }
203 
204             set
205             {
206                 ErrorUtilities.VerifyThrow(_globalRequestId == InvalidGlobalRequestId, "Global Request ID cannot be set twice.");
207                 _globalRequestId = value;
208             }
209         }
210 
211         /// <summary>
212         /// Gets the global request id of the parent request.
213         /// </summary>
214         public int ParentGlobalRequestId
215         {
216             [DebuggerStepThrough]
217             get
218             { return _parentGlobalRequestId; }
219         }
220 
221         /// <summary>
222         /// Gets the node request id
223         /// </summary>
224         public int NodeRequestId
225         {
226             [DebuggerStepThrough]
227             get
228             { return _nodeRequestId; }
229 
230             [DebuggerStepThrough]
231             set
232             { _nodeRequestId = value; }
233         }
234 
235         /// <summary>
236         /// Returns the set of unescaped targets to be built
237         /// </summary>
238         public List<string> Targets
239         {
240             [DebuggerStepThrough]
241             get
242             { return _targets; }
243         }
244 
245         /// <summary>
246         /// Returns the type of packet.
247         /// </summary>
248         public NodePacketType Type
249         {
250             [DebuggerStepThrough]
251             get
252             { return NodePacketType.BuildRequest; }
253         }
254 
255         /// <summary>
256         /// Returns the build event context of the parent, if any.
257         /// </summary>
258         public BuildEventContext ParentBuildEventContext
259         {
260             [DebuggerStepThrough]
261             get
262             { return _parentBuildEventContext; }
263         }
264 
265         /// <summary>
266         /// Returns the build event context for this request, if any.
267         /// </summary>
268         public BuildEventContext BuildEventContext
269         {
270             [DebuggerStepThrough]
271             get
272             {
273                 return _buildEventContext;
274             }
275 
276             set
277             {
278                 ErrorUtilities.VerifyThrow(_buildEventContext == BuildEventContext.Invalid, "The build event context is already set.");
279                 _buildEventContext = value;
280             }
281         }
282 
283         /// <summary>
284         /// The set of flags specified in the BuildRequestData for this request.
285         /// </summary>
286         public BuildRequestDataFlags BuildRequestDataFlags
287         {
288             get { return _buildRequestDataFlags; }
289             set { _buildRequestDataFlags = value; }
290         }
291 
292         /// <summary>
293         /// Filter describing properties, items, and metadata of interest for this request.
294         /// </summary>
295         public RequestedProjectState RequestedProjectState
296         {
297             get { return _requestedProjectState; }
298             set { _requestedProjectState = value; }
299         }
300 
301 
302         /// <summary>
303         /// The route for host-aware tasks back to the host
304         /// </summary>
305         internal HostServices HostServices
306         {
307             [DebuggerStepThrough]
308             get
309             { return _hostServices; }
310         }
311 
312         /// <summary>
313         /// Returns true if this is a root request (one which has no parent.)
314         /// </summary>
315         internal bool IsRootRequest
316         {
317             [DebuggerStepThrough]
318             get
319             { return _parentGlobalRequestId == InvalidGlobalRequestId; }
320         }
321 
322         /// <summary>
323         /// If set, skip targets that are not defined in the projects to be built.
324         /// </summary>
325         internal bool SkipNonexistentTargets
326         {
327             get { return _skipNonexistentTargets; }
328             set { _skipNonexistentTargets = value; }
329         }
330 
331         /// <summary>
332         /// Sets the configuration id to a resolved id.
333         /// </summary>
334         /// <param name="newConfigId">The new configuration id for this request.</param>
ResolveConfiguration(int newConfigId)335         public void ResolveConfiguration(int newConfigId)
336         {
337             ErrorUtilities.VerifyThrow(!IsConfigurationResolved, "Configuration already resolved");
338             _configurationId = newConfigId;
339             ErrorUtilities.VerifyThrow(IsConfigurationResolved, "Configuration not resolved");
340         }
341 
342         #region INodePacket Members
343 
344         /// <summary>
345         /// Reads/writes this packet
346         /// </summary>
Translate(INodePacketTranslator translator)347         public void Translate(INodePacketTranslator translator)
348         {
349             translator.Translate(ref _submissionId);
350             translator.Translate(ref _configurationId);
351             translator.Translate(ref _globalRequestId);
352             translator.Translate(ref _parentGlobalRequestId);
353             translator.Translate(ref _nodeRequestId);
354             translator.Translate(ref _targets);
355             translator.Translate(ref _parentBuildEventContext);
356             translator.Translate(ref _buildEventContext);
357             translator.TranslateEnum(ref _buildRequestDataFlags, (int)_buildRequestDataFlags);
358             translator.Translate(ref _skipNonexistentTargets);
359             translator.Translate(ref _requestedProjectState);
360 
361             // UNDONE: (Compat) Serialize the host object.
362         }
363 
364         /// <summary>
365         /// Factory for serialization.
366         /// </summary>
FactoryForDeserialization(INodePacketTranslator translator)367         internal static INodePacket FactoryForDeserialization(INodePacketTranslator translator)
368         {
369             return new BuildRequest(translator);
370         }
371 
372         #endregion
373         /// <summary>
374         /// Returns true if the result applies to this request.
375         /// </summary>
DoesResultApplyToRequest(BuildResult result)376         internal bool DoesResultApplyToRequest(BuildResult result)
377         {
378             return _globalRequestId == result.GlobalRequestId && _nodeRequestId == result.NodeRequestId;
379         }
380     }
381 }
382