示例#1
0
        protected override void GenerateStyleSheet(OutputGroup outputGroup, IList <InputGroup> inputGroups, UglifyCommandParser uglifyCommandParser, string outputPath, Encoding outputEncoding)
        {
            if (!FileWriteOperation(outputPath, uglifyCommandParser.IfNotNull(p => p.Clobber), () =>
            {
                // create the output file, clobbering any existing content
                using (var writer = new StreamWriter(outputPath, false, outputEncoding))
                {
                    if (inputGroups != null && inputGroups.Count > 0)
                    {
                        // for each input file, copy to the output, separating them with a newline (don't need a semicolon like JavaScript does)
                        var addSeparator = false;
                        foreach (var inputGroup in inputGroups)
                        {
                            if (addSeparator)
                            {
                                writer.WriteLine();
                            }
                            else
                            {
                                addSeparator = true;
                            }

                            writer.Write(inputGroup.Source);
                        }
                    }
                }

                return(true);
            }))
            {
                // could not write file
                Log.LogError(Strings.CouldNotWriteOutputFile, outputPath);
            }
        }
示例#2
0
        protected override void GenerateJavaScript(OutputGroup outputGroup, IList <InputGroup> inputGroups, UglifyCommandParser uglifyCommandParser, string outputPath, Encoding outputEncoding)
        {
            if (uglifyCommandParser == null)
            {
                throw new ArgumentNullException("uglifyCommandParser");
            }

            try
            {
                var settings = uglifyCommandParser.JSSettings;

                // process the resources for this output group into the settings list
                // if there are any to be processed
                if (outputGroup != null && settings != null &&
                    outputGroup.Resources.IfNotNull(rs => rs.Count > 0))
                {
                    outputGroup.ProcessResourceStrings(settings.ResourceStrings, null);
                }

                // then process the javascript output group
                ProcessJavaScript(
                    inputGroups,
                    uglifyCommandParser,
                    outputPath,
                    outputGroup.IfNotNull(og => og.SymbolMap),
                    outputEncoding);
            }
            catch (ArgumentException ex)
            {
                // processing the resource strings could throw this exception
                Log.LogError(ex.Message);
            }
        }
示例#3
0
        /// <summary>
        /// Process an output group by deleting the output files if they exist.
        /// </summary>
        /// <param name="outputGroup">the OutputGroup being processed</param>
        /// <param name="outputFileInfo">FileInfo for the desired output file</param>
        /// <param name="symbolsFileInfo">FileInfo for the optional desired symbol file</param>
        /// <param name="defaultSettings">default settings for this output group</param>
        /// <param name="manifestModifiedTime">modified time for the manifest</param>
        protected override void ProcessOutputGroup(OutputGroup outputGroup, FileInfo outputFileInfo, FileInfo symbolsFileInfo, UglifyCommandParser defaultSettings, DateTime manifestModifiedTime)
        {
            // get the settings to use -- take the configuration for this output group
            // and apply them over the default settings
            var settings = ParseConfigSettings(outputGroup.GetConfigArguments(this.Configuration), defaultSettings);

            // we really only care about the clobber setting -- if the file is read-only, don't bother deleting it
            // unless we have the clobber setting. If the current setting is Preserve, then we want to change it to
            // Auto because it makes no sense to not delete a non-readonly file during a "clean"
            var clobber = settings.Clobber == ExistingFileTreatment.Preserve
                ? ExistingFileTreatment.Auto
                : settings.Clobber;

            // we don't care about the inputs, we just want to delete the outputs and be done
            if (outputFileInfo != null)
            {
                if (!FileWriteOperation(outputFileInfo.FullName, clobber, () =>
                {
                    outputFileInfo.IfNotNull(fi =>
                    {
                        if (fi.Exists)
                        {
                            Log.LogMessage(MessageImportance.Normal, Strings.DeletingFile, fi.FullName);
                            fi.Delete();
                        }
                    });
                    return(true);
                }))
                {
                    // can't delete the file - not an error; just informational
                    Log.LogMessage(MessageImportance.Normal, Strings.CouldNotDeleteOutputFile, outputFileInfo.FullName);
                }
            }

            if (symbolsFileInfo != null)
            {
                if (!FileWriteOperation(symbolsFileInfo.FullName, clobber, () =>
                {
                    symbolsFileInfo.IfNotNull(fi =>
                    {
                        if (fi.Exists)
                        {
                            Log.LogMessage(MessageImportance.Normal, Strings.DeletingFile, fi.FullName);
                            fi.Delete();
                        }
                    });
                    return(true);
                }))
                {
                    // can't delete the file - not an error; just informational
                    Log.LogMessage(MessageImportance.Normal, Strings.CouldNotDeleteOutputFile, symbolsFileInfo.FullName);
                }
            }
        }
示例#4
0
        protected override void GenerateStyleSheet(OutputGroup outputGroup, IList <InputGroup> inputGroups, UglifyCommandParser uglifyCommandParser, string outputPath, Encoding outputEncoding)
        {
            if (uglifyCommandParser == null)
            {
                throw new ArgumentNullException("uglifyCommandParser");
            }

            ProcessStylesheet(
                inputGroups,
                uglifyCommandParser,
                outputPath,
                outputEncoding);
        }
示例#5
0
        /// <summary>
        /// Get an encoding to use for the given input file
        /// </summary>
        /// <param name="outputGroup">output file</param>
        /// <param name="defaultEncodingName">default encoding name to use if none specified</param>
        /// <returns>encoding; UTF8 WITHOUT THE BOM is the default if nothing else specified</returns>
        public static Encoding GetEncoding(this OutputGroup outputGroup, string defaultEncodingName)
        {
            Encoding encoding = null;

            if (outputGroup != null)
            {
                // if none specified on the output group, use the default
                var encodingName = outputGroup.EncodingName.IfNullOrWhiteSpace(defaultEncodingName);
                if (!encodingName.IsNullOrWhiteSpace())
                {
                    try
                    {
                        // try to create an encoding from the encoding name
                        // using special encoder fallback determined earlier, or a default
                        // encoder fallback that uses the UNICODE "replace character" for
                        // things it doesn't understand, and a decoder replacement fallback
                        // that also uses the UNICODE "replacement character" for things it doesn't understand.
                        encoding = Encoding.GetEncoding(
                            encodingName,
                            GetEncoderFallback(outputGroup.CodeType),
                            new DecoderReplacementFallback("\uFFFD"));
                    }
                    catch (ArgumentException e)
                    {
                        // eat the exception and just go with UTF-8
                        System.Diagnostics.Debug.WriteLine(e.ToString());
                    }
                }
            }

            // the default output is UTF-8 WITHOUT the BOM if we are outputting to a file,
            // or ASCII if we are outputting to STDOUT
            if (encoding == null)
            {
                if (outputGroup == null || outputGroup.Path.IsNullOrWhiteSpace())
                {
                    // no output group or outputting to stdout (no output path)
                    encoding = (Encoding)Encoding.ASCII.Clone();
                    encoding.EncoderFallback = GetEncoderFallback(outputGroup.IfNotNull(g => g.CodeType));
                }
                else
                {
                    // outputting to file, use UTF-8 WITHOUT the BOM.
                    // don't need a fallback encoder for UTF-8.
                    encoding = new UTF8Encoding(false);
                }
            }

            return(encoding);
        }
示例#6
0
        /// <summary>
        /// Create ResourceString objects from all the input resources for an output group and add them to a list
        /// </summary>
        /// <param name="outputGroup">output group</param>
        /// <param name="resourceStringList">resource strings list</param>
        /// <param name="defaultResourceObjectName">optional default resource object name</param>
        public static void ProcessResourceStrings(this OutputGroup outputGroup, IList <ResourceStrings> resourceStringList, string defaultResourceObjectName)
        {
            if (outputGroup != null && resourceStringList != null)
            {
                foreach (var resource in outputGroup.Resources)
                {
                    // create the resource strings object from the resources file.
                    var resourceStrings = ProcessResourceFile(resource.Path);

                    // if there is no name specified in the resource element, use the default.
                    resourceStrings.Name = resource.Name.IfNullOrWhiteSpace(defaultResourceObjectName);

                    // add it to the given list.
                    resourceStringList.Add(resourceStrings);
                }
            }
        }
        /// <summary>
        /// Process an output group. Override this method if the task doesn't want to check the input file times against
        /// the output file times (or existence) and call the GenerateOutput methods.
        /// </summary>
        /// <param name="outputGroup">the OutputGroup being processed</param>
        /// <param name="outputFileInfo">FileInfo for the desired output file</param>
        /// <param name="symbolsFileInfo">FileInfo for the optional desired symbol file</param>
        /// <param name="defaultSettings">default settings for this output group</param>
        /// <param name="manifestModifiedTime">modified time for the manifest</param>
        protected virtual void ProcessOutputGroup(OutputGroup outputGroup, FileInfo outputFileInfo, FileInfo symbolsFileInfo, UglifyCommandParser defaultSettings, DateTime manifestModifiedTime)
        {
            // check the file times -- if any of the inputs are newer than any output (or if any outputs don't exist),
            // then generate the output files
            if (AnyInputsAreNewerThanOutputs(outputGroup, outputFileInfo, symbolsFileInfo, manifestModifiedTime))
            {
                // get the settings to use -- take the configuration for this output group
                // and apply them over the default settings
                var settings = ParseConfigSettings(outputGroup.GetConfigArguments(this.Configuration), defaultSettings);

                GenerateOutputFiles(outputGroup, outputFileInfo, settings);
            }
            else
            {
                // none of the inputs are newer than the output -- we're good.
                Log.LogMessage(Strings.SkippedOutputFile, outputFileInfo.IfNotNull(fi => fi.Name) ?? string.Empty);
            }
        }
        void GenerateOutputFiles(OutputGroup outputGroup, FileInfo outputFileInfo, UglifyCommandParser uglifyCommandParser)
        {
            // create combined input source
            var inputGroups = outputGroup.ReadInputGroups(uglifyCommandParser.EncodingInputName);

            if (inputGroups.Count > 0)
            {
                switch (outputGroup.CodeType)
                {
                case CodeType.JavaScript:
                    // call the virtual function to generate the JavaScript output file from the inputs
                    GenerateJavaScript(outputGroup, inputGroups, uglifyCommandParser, outputFileInfo.FullName, outputGroup.GetEncoding(uglifyCommandParser.EncodingOutputName));
                    break;

                case CodeType.StyleSheet:
                    // call the virtual function to generate the stylesheet output file from the inputs
                    GenerateStyleSheet(outputGroup, inputGroups, uglifyCommandParser, outputFileInfo.FullName, outputGroup.GetEncoding(uglifyCommandParser.EncodingOutputName));
                    break;

                case CodeType.Unknown:
                    Log.LogError(Strings.UnknownCodeType);
                    break;
                }
            }
            else
            {
                // no input! write an empty output file
                if (!FileWriteOperation(outputFileInfo.FullName, uglifyCommandParser.Clobber, () =>
                {
                    using (var stream = outputFileInfo.Create())
                    {
                        // write nothing; just create the empty file
                        return(true);
                    }
                }))
                {
                    // could not write file
                    Log.LogError(Strings.CouldNotWriteOutputFile, outputFileInfo.FullName);
                }
            }
        }
示例#9
0
 protected override void GenerateStyleSheet(OutputGroup outputGroup, IList <InputGroup> inputGroups, UglifyCommandParser uglifyCommandParser, string outputPath, Encoding outputEncoding)
 {
     // shouldn't get called because we override the ProcessOutputGroup method
     throw new NotImplementedException();
 }
        /// <summary>
        /// Check the file times of the input files and the output files. If the output files don't exist, or if any of the input files are
        /// newer than the already-existing output files, generate the output files again by calling the virtual method GenerateOutputFiles.
        /// </summary>
        /// <param name="outputGroup">the OutputGroup being processed</param>
        /// <param name="outputFileInfo">FileInfo for the desired output file</param>
        /// <param name="symbolsFileInfo">FileInfo for the optional desired symbol file</param>
        /// <param name="manifestModifiedTime">modified time for the manifest</param>
        bool AnyInputsAreNewerThanOutputs(OutputGroup outputGroup, FileInfo outputFileInfo, FileInfo symbolsFileInfo, DateTime manifestModifiedTime)
        {
            // build the output files
            var processGroup = false;
            var codeType     = outputGroup.CodeType;

            if (!outputFileInfo.Exists ||
                (!IgnoreSourceMapOutput && symbolsFileInfo != null && !symbolsFileInfo.Exists))
            {
                // one or more outputs don't exist, so we need to process this group
                processGroup = true;
            }
            else
            {
                // output exists. we need to check to see if it's older than
                // any of its input files, and if not, there's no need to process
                // this group. get the filetime of the output file.
                var outputFileTime = outputFileInfo.LastWriteTimeUtc;

                // if we don't want a symbol map, then ignore that output. But if we
                // do and it doesn't exist, then we want to process the group. If we
                // do and it does, then check its filetime and set out output filetime
                // to be the earliest of the two (output or symbols)
                if (!IgnoreSourceMapOutput && symbolsFileInfo != null)
                {
                    var symbolsFileTime = symbolsFileInfo.LastWriteTimeUtc;
                    if (symbolsFileTime < outputFileTime)
                    {
                        outputFileTime = symbolsFileTime;
                    }
                }

                // first check the time of the manifest file itself. If it's newer than the output
                // time, then we need to process. Otherwise we need to check each input source file.
                // also rebuild if they're equal, just in case.
                if (manifestModifiedTime >= outputFileTime)
                {
                    // the manifest itself has been changed after the last output that was generated,
                    // so yes: we need to process this group.
                    processGroup = true;
                }
                else
                {
                    // check filetime of each input file, and if ANY one is newer,
                    // then we will want to set the process-group flag and stop checking
                    foreach (var input in outputGroup.Inputs)
                    {
                        var fileInfo = new FileInfo(input.Path);
                        if (fileInfo.Exists)
                        {
                            // equal time also means process the output, because that is just SO close to
                            // the input being newer. Plus, someone might have the input be the output, so
                            // the times will be equal and we will want to process it.
                            if (fileInfo.LastWriteTimeUtc >= outputFileTime)
                            {
                                processGroup = true;
                                break;
                            }
                        }
                        else
                        {
                            // file doesn't exist -- check to see if it's a directory
                            var folderInfo = new DirectoryInfo(fileInfo.FullName);
                            if (folderInfo.Exists)
                            {
                                // not a FILE, it's a FOLDER of files.
                                // in order to specify an input folder, we need to have had the right type attribute
                                // on the output group so we know what kind of files to look for
                                if (codeType == CodeType.Unknown)
                                {
                                    // log an error, then bail because we won't be able to do anything anyway
                                    // since we don't know what kind of code we are processing and we don't know which
                                    // files to include from this folder.
                                    Log.LogError(Strings.DirectorySourceRequiresCodeType);
                                    return(false);
                                }
                                else
                                {
                                    // recursively check all the files in the folder with the proper extension for the code type.
                                    // if anything pops positive, we know we want to process the group so bail early.
                                    processGroup = CheckFolderInputFileTimes(folderInfo, ExtensionsFromCodeType(codeType), outputFileTime);
                                    if (processGroup)
                                    {
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }

                // do the same to any resource file, there are any (and we don't already know we
                // want to process this group)
                if (!processGroup && outputGroup.Resources.Count > 0)
                {
                    foreach (var resource in outputGroup.Resources)
                    {
                        var fileInfo = new FileInfo(resource.Path);
                        if (fileInfo.Exists && fileInfo.LastWriteTimeUtc >= outputFileTime)
                        {
                            processGroup = true;
                            break;
                        }
                    }
                }
            }

            return(processGroup);
        }
 protected abstract void GenerateStyleSheet(OutputGroup outputGroup, IList <InputGroup> inputGroups, UglifyCommandParser uglifyCommandParser, string outputPath, Encoding outputEncoding);
示例#12
0
        /// <summary>
        /// Walk the list of inputs for a given output group, and create a list of input groups
        /// consisting of the source and origin. Consecutive project inputs are concatenated together,
        /// and external inputs are kept separate.
        /// </summary>
        /// <param name="outputGroup">output group</param>
        /// <param name="defaultEncodingName">default encoding name to use if none specified</param>
        /// <param name="rawInputBuilder">output the raw source to this builder, no added comments</param>
        /// <param name="sourceLength">length of the raw source</param>
        /// <returns>list of input groups consisting of source code and origin</returns>
        public static IList <InputGroup> ReadInputGroups(this OutputGroup outputGroup, string defaultEncodingName)
        {
            var inputGroups = new List <InputGroup>();

            if (outputGroup != null)
            {
                if (outputGroup.Inputs.Count == 0)
                {
                    try
                    {
                        // try setting the input encoding - sometimes this fails!
                        Console.InputEncoding = GetInputEncoding(defaultEncodingName);
                    }
                    catch (IOException e)
                    {
                        // error setting the encoding input; just use whatever the default is
                        System.Diagnostics.Debug.WriteLine(e.ToString());
                    }

                    var sourceComment = outputGroup.CodeType == CodeType.StyleSheet
                        ? "/*/#SOURCE 1 1 stdin */\r\n"
                        : "///#SOURCE 1 1 stdin\r\n";

                    // read from stdin, append to the builder, then create a single project input group
                    // from the build code.
                    var sourceCode = Console.In.ReadToEnd();
                    inputGroups.Add(new InputGroup
                    {
                        RawSource = sourceCode,
                        Source    = sourceComment + sourceCode,
                        Origin    = SourceOrigin.Project
                    });
                }
                else
                {
                    // start off saying the previous item ends in a semicolon so we don't add one before the first file
                    var fileReadBuilder = new FileReadBuilder {
                        EndsInSemicolon = true
                    };
                    for (var ndx = 0; ndx < outputGroup.Inputs.Count; ++ndx)
                    {
                        var inputFile = outputGroup.Inputs[ndx];
                        if (inputFile.Origin == SourceOrigin.External)
                        {
                            // we found an external input file.
                            // if we were building up project input files, dump what we have into a new group and clear it out
                            if (fileReadBuilder.Length > 0)
                            {
                                inputGroups.Add(new InputGroup
                                {
                                    Source    = fileReadBuilder.ToString(),
                                    RawSource = fileReadBuilder.ToRawString(),
                                    Origin    = SourceOrigin.Project
                                });
                            }

                            // read the content of the external file, passing in false for whether the previous file
                            // ended in a semicolon so that one will ALWAYS get prepended. Add a new input group for the
                            // content by itself, and then always reset the semicolon flag to false so that the NEXT code
                            // will be separated from the external code with another semicolon.
                            fileReadBuilder.Clear();
                            ReadFile(inputFile, fileReadBuilder, outputGroup.CodeType, defaultEncodingName);
                            inputGroups.Add(new InputGroup
                            {
                                Source    = fileReadBuilder.ToString(),
                                RawSource = fileReadBuilder.ToRawString(),
                                Origin    = SourceOrigin.External
                            });

                            fileReadBuilder.Clear();
                            fileReadBuilder.EndsInSemicolon = false;
                        }
                        else
                        {
                            // read the project input file into the group builder, with context.
                            ReadFile(inputFile, fileReadBuilder, outputGroup.CodeType, defaultEncodingName);
                        }
                    }

                    // if there is anything left, add it as a new input group now
                    if (fileReadBuilder.Length > 0)
                    {
                        inputGroups.Add(new InputGroup
                        {
                            Source    = fileReadBuilder.ToString(),
                            RawSource = fileReadBuilder.ToRawString(),
                            Origin    = SourceOrigin.Project
                        });
                    }
                }
            }

            return(inputGroups);
        }
示例#13
0
 /// <summary>
 /// Given the current configuration, return the appropriate argument string
 /// </summary>
 /// <param name="outputGroup">output group object</param>
 /// <param name="configuration">current configuration</param>
 /// <returns>argument string, or empty string if not found</returns>
 public static string GetConfigArguments(this OutputGroup outputGroup, string configuration)
 {
     return(outputGroup == null ? string.Empty : GetConfigArguments(outputGroup.Arguments, configuration));
 }
示例#14
0
        private static OutputGroup ReadOutputElement(XmlReader reader)
        {
            var outputNode = new OutputGroup();

            while (reader.Read())
            {
                // get the attributes
                if (reader.Name == OutputElementName && reader.HasAttributes)
                {
                    while (reader.MoveToNextAttribute())
                    {
                        switch (reader.Name)
                        {
                        case PathAttributeName:
                            outputNode.Path = reader.Value;
                            break;

                        case EncodingAttributeName:
                        case EncodingAttributeShortName:
                            outputNode.EncodingName = reader.Value;
                            break;

                        case TypeAttributeName:
                            switch (reader.Value.ToUpperInvariant())
                            {
                            case "JS":
                            case "JAVASCRIPT":
                            case "JSCRIPT":
                                outputNode.CodeType = CodeType.JavaScript;
                                break;

                            case "CSS":
                            case "STYLESHEET":
                            case "STYLESHEETS":
                                outputNode.CodeType = CodeType.StyleSheet;
                                break;
                            }
                            break;

                        case MapPathAttributeName:
                            outputNode.SymbolMap = new SymbolMap()
                            {
                                Path = reader.Value
                            };
                            break;
                        }
                    }

                    // back to element
                    reader.MoveToElement();
                }

                // process child elements
                if (reader.NodeType == XmlNodeType.Element)
                {
                    switch (reader.Name)
                    {
                    case ArgumentsElementName:
                        ReadArgumentsElement(reader.ReadSubtree(), outputNode.Arguments);
                        break;

                    case RenameElementName:
                        ReadRenameElement(reader.ReadSubtree(), outputNode.RenameIdentifiers);
                        break;

                    case NoRenameElementName:
                        ReadNoRenameElement(reader.ReadSubtree(), outputNode.NoRenameIdentifiers);
                        break;

                    case SymbolMapElementName:
                        outputNode.SymbolMap = ReadSymbolMapElement(reader.ReadSubtree());
                        break;

                    case ResourceElementName:
                        outputNode.Resources.Add(ReadResourceElement(reader.ReadSubtree()));
                        break;

                    case InputElementName:
                        outputNode.Inputs.Add(ReadInputElement(reader.ReadSubtree()));
                        break;
                    }
                }
            }

            ((IDisposable)reader).Dispose();
            return(outputNode);
        }