private async Task<ITargetDescription> _performCreateTargetDescription(PreflightResult result, ISource source, CancellationToken token, SelfAssemblyMode mode) { Debug.Assert(result.ErrorMessage == null, "TargetDescription ordered despite the preflight result containing errors."); Debug.Assert(result.References.All(r => r.ErrorMessage == null), "TargetDescription ordered despite the preflight result containing errors."); RefSpec[] refSpecs; switch (mode) { case SelfAssemblyMode.RecurseIntoFileSystem: var refSpecResolveTasks = result.References .Select(r => _resolveRefSpec(r, token, mode)) .ToArray(); await Task.WhenAll(refSpecResolveTasks); refSpecs = refSpecResolveTasks.Select(t => t.Result).ToArray(); break; case SelfAssemblyMode.RegisterOnly: refSpecs = result.References.Select(_forbidFileRefSpec).ToArray(); break; default: throw new ArgumentOutOfRangeException("mode", mode, Resources.SelfAssemblingPlan_performCreateTargetDescription_mode); } var buildMessages = refSpecs.Where(t => t.ErrorMessage != null).Select( s => { Debug.Assert(s.ErrorMessage != null); // ReSharper disable PossibleNullReferenceException,AssignNullToNotNullAttribute var refPosition = new SourcePosition( s.ResolvedPath != null ? s.ResolvedPath.ToString() : result.Path != null ? result.Path.ToString() : NoSourcePosition.MissingFileName, 0, 0); return Message.Error(s.ErrorMessage, refPosition, MessageClasses.SelfAssembly); // ReSharper restore PossibleNullReferenceException,AssignNullToNotNullAttribute }); // Assemble dependencies, including standard library (unless suppressed) var deps = refSpecs.Where(r => r.ModuleName != null).Select(r => r.ModuleName); if (!result.SuppressStandardLibrary) deps = deps.Append(StandardLibrary); var reportedFileName = result.Path != null ? result.Path.ToString() : result.ModuleName != null ? result.ModuleName.Id + ".pxs" : null; // Typically, duplicate requests are caught much earlier (based on full file paths) // But if the user of this self assembling build plan manually adds descriptions // that can also be found on the file system, that conflict can in some situations // not be detected until full preflight is done. // This GetOrAdd is our last line of defense against that scenario and race conditions // around targets in general (e.g., when symbolic links or duplicate files are involved) return TargetDescriptions.GetOrAdd(result.ModuleName, mn => CreateDescription(mn, source, reportedFileName, deps, buildMessages)); }
private async Task<RefSpec> _resolveRefSpec([NotNull] RefSpec refSpec, CancellationToken token, SelfAssemblyMode mode) // requires refSpec.ModuleName != null || refSpec.Source != null || refSpec.rawPath != null || refSpec.ResolvedPath != null // ensures result == refSpec && (TargetDescriptions.Contains(result) || refSpec.ErrorMessage != null) { if (refSpec.ErrorMessage != null) return refSpec; var pathCandidateCount = 0; IEnumerator<FileInfo> candidateSequence = null; var expectedModuleName = refSpec.ModuleName; try { while (refSpec.ModuleName == null || !TargetDescriptions.Contains(refSpec.ModuleName)) { if (candidateSequence == null) candidateSequence = _pathCandidates(refSpec, token).GetEnumerator(); if (!candidateSequence.MoveNext()) { var msg = String.Format( "Failed to find a file that matches the reference specification {0}. {1} path(s) searched.", refSpec, pathCandidateCount); _trace.TraceEvent(TraceEventType.Error, 0, msg); refSpec.ErrorMessage = msg; break; } var candidate = candidateSequence.Current; pathCandidateCount++; refSpec.ResolvedPath = candidate; var result = await _orderPreflight(refSpec, token); if (result.ErrorMessage != null) { _trace.TraceEvent(TraceEventType.Verbose, 0, "Rejected {0} as a candidate for {1} because there were errors during preflight: {2}", candidate, refSpec, result.ErrorMessage); } else if (result.ModuleName == null) { _trace.TraceEvent(TraceEventType.Information, 0, "Rejected {0} as a candidate for {1} because its module name could not be inferred.", candidate, refSpec); } else if (expectedModuleName != null && !Engine.StringsAreEqual(result.ModuleName.Id, expectedModuleName.Id)) { _trace.TraceEvent(TraceEventType.Warning, 0, "Rejected {0} as a candidate for {1} because the module name in the file ({2}) doesn't match the module name expected by the reference.", candidate, refSpec, result.ModuleName.Id); } else { refSpec.ModuleName = result.ModuleName; _trace.TraceEvent(TraceEventType.Information, 0, "Accepted match {0} after preflight, ordering corresponding description.", result.ModuleName); await _orderTargetDescription(result, candidate, token, mode); } } } finally { if (candidateSequence != null) candidateSequence.Dispose(); } Debug.Assert(refSpec.ErrorMessage != null || TargetDescriptions.Contains(refSpec.ModuleName)); return refSpec; }
private Task<ITargetDescription> _orderTargetDescription(PreflightResult result, FileInfo candidate, CancellationToken token, SelfAssemblyMode mode) { if (candidate == null) throw new ArgumentNullException("candidate"); return _targetCreationCache.GetOrAdd(candidate.FullName, async (key, actualToken) => { var src = Source.FromFile(candidate, Encoding); await Task.Yield(); await _addToSearchPaths(candidate, actualToken); return await _performCreateTargetDescription(result, src, actualToken, mode); }, token); }
private async Task<ITargetDescription> _assembleAsync(ISource source, CancellationToken token, SelfAssemblyMode mode) { token.ThrowIfCancellationRequested(); // Technically _orderPreflight would be better, but that only works with a resolved path as the key var primaryPreflight = await _performPreflight(new RefSpec {Source = source, ResolvedPath = _getPath(source)}, token); if (primaryPreflight.ErrorMessage != null) { var errorMessage = Message.Error(primaryPreflight.ErrorMessage, NoSourcePosition.Instance, MessageClasses.SelfAssembly); if (primaryPreflight.ModuleName != null) { return CreateDescription(primaryPreflight.ModuleName, Source.FromString(""), NoSourcePosition.MissingFileName, Enumerable.Empty<ModuleName>(), new[] {errorMessage}); } else { throw new BuildFailureException(null, "There {2} {0} {1} while trying to determine dependencies.", new[] {errorMessage}); } } else { token.ThrowIfCancellationRequested(); return await _performCreateTargetDescription(primaryPreflight, source, token, mode); } }