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> _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<PreflightResult> _performPreflight(RefSpec refSpec, CancellationToken token) // requires refSpec.Source != null // ensures result != null { if (refSpec.ErrorMessage != null) return new PreflightResult { ErrorMessage = refSpec.ErrorMessage }; token.ThrowIfCancellationRequested(); _trace.TraceEvent(TraceEventType.Information, 0, "Preflight parsing of {0} requested.", refSpec); // Make sure refSpec has a re-usable source (will have to support both preflight and actual compilation) var source = refSpec.Source; if (source == null) throw new ArgumentException(Resources.SelfAssemblingPlan_RefSpecMustHaveSource, "refSpec"); var reportedPath = _getPath(source); if (source.IsSingleUse) source = await source.CacheInMemoryAsync(); token.ThrowIfCancellationRequested(); // Perform preflight parse var eng = new Engine { ExecutionProhibited = true }; var app = new Application(); var ldr = new Loader(new LoaderOptions(eng, app) { // Important: Have preflight flag set PreflightModeEnabled = true, ReconstructSymbols = false, RegisterCommands = false, StoreSourceInformation = false, }); TextReader sourceReader; if (!source.TryOpen(out sourceReader)) { var errorResult = new PreflightResult { ErrorMessage = "Failed to open " + refSpec + " for preflight parsing." }; return errorResult; } ldr.LoadFromReader(sourceReader, refSpec.ResolvedPath != null ? refSpec.ResolvedPath.ToString() : null); // Extract preflight information ModuleName theModuleName; if (!ModuleName.TryParse(app.Meta[Module.NameKey], out theModuleName)) theModuleName = app.Module.Name; MetaEntry noStdLibEntry; var result = new PreflightResult { ModuleName = theModuleName, SuppressStandardLibrary = app.Meta.TryGetValue(Module.NoStandardLibraryKey, out noStdLibEntry) && noStdLibEntry.Switch, Path = reportedPath }; result.References.AddRange( app.Meta[Module.ReferencesKey].List.Where(entry => !entry.Equals(new MetaEntry(""))) .Select(_parseRefSpec)); _trace.TraceEvent(TraceEventType.Verbose, 0, "Preflight parsing of {0} finished.", refSpec); return result; }