Exemplo n.º 1
0
        private PipelineBuildEvent LoadBuildEvent(string destFile, out string eventFilepath)
        {
            var contentPath = Path.ChangeExtension(PathHelper.GetRelativePath(OutputDirectory, destFile), PipelineBuildEvent.Extension);

            eventFilepath = Path.Combine(IntermediateDirectory, contentPath);
            return(PipelineBuildEvent.Load(eventFilepath));
        }
Exemplo n.º 2
0
        public async Task <PipelineBuildEvent> BuildContent(
            string sourceFilepath,
            string outputFilepath = null,
            string importerName   = null,
            string processorName  = null,
            OpaqueDataDictionary processorParameters = null)
        {
            sourceFilepath = PathHelper.Normalize(sourceFilepath);
            ResolveOutputFilepath(sourceFilepath, ref outputFilepath);

            ResolveImporterAndProcessor(sourceFilepath, ref importerName, ref processorName);

            // Record what we're building and how.
            var contentEvent = new PipelineBuildEvent
            {
                SourceFile = sourceFilepath,
                DestFile   = outputFilepath,
                Importer   = importerName,
                Processor  = processorName,
                Parameters = ValidateProcessorParameters(processorName, processorParameters),
            };

            // Load the previous content event if it exists.
            string eventFilepath;
            var    cachedEvent = LoadBuildEvent(contentEvent.DestFile, out eventFilepath);

            await BuildContent(contentEvent, cachedEvent, eventFilepath);

            return(contentEvent);
        }
        public override TOutput BuildAndLoadAsset <TInput, TOutput>(ExternalReference <TInput> sourceAsset,
                                                                    string processorName,
                                                                    OpaqueDataDictionary processorParameters,
                                                                    string importerName)
        {
            var sourceFilepath = PathHelper.Normalize(sourceAsset.Filename);

            // The processorName can be null or empty. In this case the asset should
            // be imported but not processed. This is, for example, necessary to merge
            // animation files as described here:
            // http://blogs.msdn.com/b/shawnhar/archive/2010/06/18/merging-animation-files.aspx.
            bool processAsset = !string.IsNullOrEmpty(processorName);

            _manager.ResolveImporterAndProcessor(sourceFilepath, ref importerName, ref processorName);

            var buildEvent = new PipelineBuildEvent
            {
                SourceFile = sourceFilepath,
                Importer   = importerName,
                Processor  = processAsset ? processorName : null,
                Parameters = _manager.ValidateProcessorParameters(processorName, processorParameters),
            };

            var processedObject = _manager.ProcessContent(buildEvent);

            // Record that we processed this dependent asset.
            _pipelineEvent.Dependencies.AddUnique(sourceFilepath);

            return((TOutput)processedObject);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Stores the pipeline build event (in memory) if no matching event is found.
        /// </summary>
        /// <param name="pipelineEvent">The pipeline build event.</param>
        private void TrackPipelineBuildEvent(PipelineBuildEvent pipelineEvent)
        {
            List <PipelineBuildEvent> pipelineBuildEvents;
            bool eventsFound = _pipelineBuildEvents.TryGetValue(pipelineEvent.SourceFile, out pipelineBuildEvents);

            if (!eventsFound)
            {
                pipelineBuildEvents = new List <PipelineBuildEvent>();
                _pipelineBuildEvents.Add(pipelineEvent.SourceFile, pipelineBuildEvents);
            }

            if (FindMatchingEvent(pipelineBuildEvents, pipelineEvent.DestFile, pipelineEvent.Importer, pipelineEvent.Processor, pipelineEvent.Parameters) == null)
            {
                pipelineBuildEvents.Add(pipelineEvent);
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Determines whether the specified list contains a matching pipeline build event.
        /// </summary>
        /// <param name="pipelineBuildEvents">The list of pipeline build events.</param>
        /// <param name="destFile">Absolute path to the output file. Can be <see langword="null"/>.</param>
        /// <param name="importerName">The name of the content importer. Can be <see langword="null"/>.</param>
        /// <param name="processorName">The name of the content processor. Can be <see langword="null"/>.</param>
        /// <param name="processorParameters">The processor parameters. Can be <see langword="null"/>.</param>
        /// <returns>
        /// The matching pipeline build event, or <see langword="null"/>.
        /// </returns>
        private PipelineBuildEvent FindMatchingEvent(List <PipelineBuildEvent> pipelineBuildEvents, string destFile, string importerName, string processorName, OpaqueDataDictionary processorParameters)
        {
            foreach (var existingBuildEvent in pipelineBuildEvents)
            {
                if ((destFile == null || existingBuildEvent.DestFile.Equals(destFile)) &&
                    existingBuildEvent.Importer == importerName &&
                    existingBuildEvent.Processor == processorName)
                {
                    var defaultValues = GetProcessorDefaultValues(processorName);
                    if (PipelineBuildEvent.AreParametersEqual(existingBuildEvent.Parameters, processorParameters, defaultValues))
                    {
                        return(existingBuildEvent);
                    }
                }
            }

            return(null);
        }
Exemplo n.º 6
0
        public void RegisterContent(string sourceFilepath, string outputFilepath = null, string importerName = null, string processorName = null, OpaqueDataDictionary processorParameters = null)
        {
            sourceFilepath = PathHelper.Normalize(sourceFilepath);
            ResolveOutputFilepath(sourceFilepath, ref outputFilepath);

            ResolveImporterAndProcessor(sourceFilepath, ref importerName, ref processorName);

            var contentEvent = new PipelineBuildEvent
            {
                SourceFile = sourceFilepath,
                DestFile   = outputFilepath,
                Importer   = importerName,
                Processor  = processorName,
                Parameters = ValidateProcessorParameters(processorName, processorParameters),
            };

            // Register pipeline build event. (Required to correctly resolve external dependencies.)
            TrackPipelineBuildEvent(contentEvent);
        }
Exemplo n.º 7
0
        private async Task WriteXnb(object content, PipelineBuildEvent pipelineEvent)
        {
            // Make sure the output directory exists.
            var outputFileDir = Path.GetDirectoryName(pipelineEvent.DestFile);

            Directory.CreateDirectory(outputFileDir);

            if (_compiler == null)
            {
                _compiler = new ContentCompiler();
            }

            // Write the XNB.
            using (var stream = new FileStream(pipelineEvent.DestFile, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                await Task.Run(() => _compiler.Compile(stream, content, Platform, Profile, CompressContent, OutputDirectory, outputFileDir));
            }

            // Store the last write time of the output XNB here
            // so we can verify it hasn't been tampered with.
            pipelineEvent.DestTime = File.GetLastWriteTime(pipelineEvent.DestFile);
        }
Exemplo n.º 8
0
        public object ProcessContent(PipelineBuildEvent pipelineEvent)
        {
            if (!File.Exists(pipelineEvent.SourceFile))
            {
                throw new PipelineException("The source file '{0}' does not exist!", pipelineEvent.SourceFile);
            }

            // Store the last write time of the source file
            // so we can detect if it has been changed.
            pipelineEvent.SourceTime = File.GetLastWriteTime(pipelineEvent.SourceFile);

            // Make sure we can find the importer and processor.
            var importer = CreateImporter(pipelineEvent.Importer);

            if (importer == null)
            {
                throw new PipelineException("Failed to create importer '{0}'", pipelineEvent.Importer);
            }

            // Try importing the content.
            object importedObject;

            if (RethrowExceptions)
            {
                try
                {
                    var importContext = new PipelineImporterContext(this);
                    importedObject = importer.Import(pipelineEvent.SourceFile, importContext);
                }
                catch (PipelineException)
                {
                    throw;
                }
                catch (Exception inner)
                {
                    throw new PipelineException(string.Format("Importer '{0}' had unexpected failure!", pipelineEvent.Importer), inner);
                }
            }
            else
            {
                var importContext = new PipelineImporterContext(this);
                importedObject = importer.Import(pipelineEvent.SourceFile, importContext);
            }

            // The pipelineEvent.Processor can be null or empty. In this case the
            // asset should be imported but not processed.
            if (string.IsNullOrEmpty(pipelineEvent.Processor))
            {
                return(importedObject);
            }

            var processor = CreateProcessor(pipelineEvent.Processor, pipelineEvent.Parameters);

            if (processor == null)
            {
                throw new PipelineException("Failed to create processor '{0}'", pipelineEvent.Processor);
            }

            // Make sure the input type is valid.
            if (!processor.InputType.IsAssignableFrom(importedObject.GetType()))
            {
                throw new PipelineException(
                          string.Format("The type '{0}' cannot be processed by {1} as a {2}!",
                                        importedObject.GetType().FullName,
                                        pipelineEvent.Processor,
                                        processor.InputType.FullName));
            }

            // Process the imported object.

            object processedObject;

            if (RethrowExceptions)
            {
                try
                {
                    var processContext = new PipelineProcessorContext(this, pipelineEvent);
                    processedObject = processor.Process(importedObject, processContext);
                }
                catch (PipelineException)
                {
                    throw;
                }
                catch (InvalidContentException)
                {
                    throw;
                }
                catch (Exception inner)
                {
                    throw new PipelineException(string.Format("Processor '{0}' had unexpected failure!", pipelineEvent.Processor), inner);
                }
            }
            else
            {
                var processContext = new PipelineProcessorContext(this, pipelineEvent);
                processedObject = processor.Process(importedObject, processContext);
            }

            return(processedObject);
        }
Exemplo n.º 9
0
        private async Task BuildContent(
            PipelineBuildEvent pipelineEvent,
            PipelineBuildEvent cachedEvent,
            string eventFilepath)
        {
            if (!File.Exists(pipelineEvent.SourceFile))
            {
                Logger.LogMessage("{0}", pipelineEvent.SourceFile);
                throw new PipelineException("The source file '{0}' does not exist!", pipelineEvent.SourceFile);
            }

            Logger.PushFile(pipelineEvent.SourceFile);

            // Keep track of all build events. (Required to resolve automatic names "AssetName_n".)
            TrackPipelineBuildEvent(pipelineEvent);

            var rebuild = pipelineEvent.NeedsRebuild(this, cachedEvent);

            if (rebuild)
            {
                Logger.LogMessage("{0}", pipelineEvent.SourceFile);
            }
            else
            {
                Logger.LogMessage("Skipping {0}", pipelineEvent.SourceFile);
            }

            Logger.Indent();
            try
            {
                if (!rebuild)
                {
                    // While this asset doesn't need to be rebuilt the dependent assets might.
                    foreach (var asset in cachedEvent.BuildAsset)
                    {
                        string assetEventFilepath;
                        var    assetCachedEvent = LoadBuildEvent(asset, out assetEventFilepath);

                        // If we cannot find the cached event for the dependancy
                        // then we have to trigger a rebuild of the parent content.
                        if (assetCachedEvent == null)
                        {
                            rebuild = true;
                            break;
                        }

                        var depEvent = new PipelineBuildEvent
                        {
                            SourceFile = assetCachedEvent.SourceFile,
                            DestFile   = assetCachedEvent.DestFile,
                            Importer   = assetCachedEvent.Importer,
                            Processor  = assetCachedEvent.Processor,
                            Parameters = assetCachedEvent.Parameters,
                        };

                        // Give the asset a chance to rebuild.
                        await BuildContent(depEvent, assetCachedEvent, assetEventFilepath);
                    }
                }

                // Do we need to rebuild?
                if (rebuild)
                {
                    var startTime = DateTime.UtcNow;

                    // Import and process the content.
                    var processedObject = ProcessContent(pipelineEvent);

                    // Write the content to disk.
                    await WriteXnb(processedObject, pipelineEvent);

                    // Store the timestamp of the DLLs containing the importer and processor.
                    pipelineEvent.ImporterTime  = GetImporterAssemblyTimestamp(pipelineEvent.Importer);
                    pipelineEvent.ProcessorTime = GetProcessorAssemblyTimestamp(pipelineEvent.Processor);

                    // Store the new event into the intermediate folder.
                    pipelineEvent.Save(eventFilepath);

                    var buildTime = DateTime.UtcNow - startTime;

                    // Record stat for this file.
                    ContentStats.RecordStats(pipelineEvent.SourceFile, pipelineEvent.DestFile, pipelineEvent.Processor, processedObject.GetType(), (float)buildTime.TotalSeconds);
                }
                else
                {
                    // Copy the stats from the previous build.
                    ContentStats.CopyPreviousStats(pipelineEvent.SourceFile);
                }
            }
            finally
            {
                Logger.Unindent();
                Logger.PopFile();
            }
        }
Exemplo n.º 10
0
        public bool NeedsRebuild(PipelineManager manager, PipelineBuildEvent cachedEvent)
        {
            // If we have no previously cached build event then we cannot
            // be sure that the state hasn't changed... force a rebuild.
            if (cachedEvent == null)
            {
                return(true);
            }

            // Verify that the last write time of the source file matches
            // what we recorded when it was built.  If it is different
            // that means someone modified it and we need to rebuild.
            var sourceWriteTime = File.GetLastWriteTime(SourceFile);

            if (cachedEvent.SourceTime != sourceWriteTime)
            {
                return(true);
            }

            // Do the same test for the dest file.
            var destWriteTime = File.GetLastWriteTime(DestFile);

            if (cachedEvent.DestTime != destWriteTime)
            {
                return(true);
            }

            // If the source file is newer than the dest file
            // then it must have been updated and needs a rebuild.
            if (sourceWriteTime >= destWriteTime)
            {
                return(true);
            }

            // Are any of the dependancy files newer than the dest file?
            foreach (var depFile in cachedEvent.Dependencies)
            {
                if (File.GetLastWriteTime(depFile) >= destWriteTime)
                {
                    return(true);
                }
            }

            // This shouldn't happen...  but if the source or dest files changed
            // then force a rebuild.
            if (cachedEvent.SourceFile != SourceFile ||
                cachedEvent.DestFile != DestFile)
            {
                return(true);
            }

            // Did the importer assembly change?
            if (manager.GetImporterAssemblyTimestamp(cachedEvent.Importer) > cachedEvent.ImporterTime)
            {
                return(true);
            }

            // Did the importer change?
            if (cachedEvent.Importer != Importer)
            {
                return(true);
            }

            // Did the processor assembly change?
            if (manager.GetProcessorAssemblyTimestamp(cachedEvent.Processor) > cachedEvent.ProcessorTime)
            {
                return(true);
            }

            // Did the processor change?
            if (cachedEvent.Processor != Processor)
            {
                return(true);
            }

            // Did the parameters change?
            var defaultValues = manager.GetProcessorDefaultValues(Processor);

            if (!AreParametersEqual(cachedEvent.Parameters, Parameters, defaultValues))
            {
                return(true);
            }

            return(false);
        }
 public PipelineProcessorContext(PipelineManager manager, PipelineBuildEvent pipelineEvent)
 {
     _manager       = manager;
     _pipelineEvent = pipelineEvent;
 }