/// <summary>
        /// Creates a log file if any files were generated in a different folder or project.
        /// </summary>
        /// <param name="outputFiles">
        /// A collection of <see cref="OutputFile"/> objects.
        /// </param>
        /// <param name="template">
        /// A <see cref="ProjectItem"/> that represents T4 template being transformed.
        /// </param>
        /// <remarks>
        /// A log file is created as an <see cref="OutputFile"/> object and added to
        /// the collection. The <see cref="UpdateOutputFiles"/> method takes care of
        /// saving it.
        /// </remarks>
        private static void CreateLogIfNecessary(ICollection <OutputFile> outputFiles, ProjectItem template)
        {
            string templateProject   = GetPath(template.ContainingProject);
            string templateDirectory = Environment.CurrentDirectory;

            //Only create a log file when the directories or the projects are different.
            if (outputFiles.Any(output =>
                                !OutputInfo.SamePath(Path.GetDirectoryName(output.File), templateDirectory) ||
                                (!string.IsNullOrEmpty(output.Project) && !OutputInfo.SamePath(output.Project, templateProject))))
            {
                OutputFile log = new OutputFile();
                log.File    = GetLogFileName();
                log.Project = string.Empty;
                outputFiles.Add(log);

                log.Content.AppendLine("// <autogenerated>");
                log.Content.AppendLine("//   This file contains the list of files generated by " + Path.GetFileName(Host.TemplateFile) + ".");
                log.Content.AppendLine("//   It is used by T4 Toolbox (http://www.codeplex.com/t4toolbox) to automatically delete");
                log.Content.AppendLine("//   generated files that are no longer necessary. Do not modify this file manually. Manual");
                log.Content.AppendLine("//   changes may cause orphaned files and will be lost next time the code is regenerated.");
                log.Content.AppendLine("// </autogenerated>");
                foreach (OutputFile output in outputFiles)
                {
                    // Don't log the generated files that will be preserved
                    if (!output.PreserveExistingFile)
                    {
                        log.Content.AppendLine(GetRelativePath(Host.TemplateFile, output.File));
                    }
                }
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Appends generated <paramref name="content"/> to the specified <see cref="OutputInfo.File"/>.
        /// </summary>
        /// <param name="output">
        /// An <see cref="OutputInfo"/> object that describes content generated by a template.
        /// </param>
        /// <param name="content">
        /// A <see cref="String"/> that contains content generated by a template.
        /// </param>
        /// <param name="host">
        /// An <see cref="ITextTemplatingEngineHost"/> object hosting the transformation.
        /// </param>
        private void AppendToOutputFile(OutputInfo output, string content, ITextTemplatingEngineHost host)
        {
            // If some content was already generated for this file
            string     filePath   = GetFullFilePath(output, host);
            OutputFile outputFile = this.outputFiles.FirstOrDefault(o => OutputInfo.SamePath(o.File, filePath));

            if (outputFile != null)
            {
                // Verify that output properties match
                Validate(output, outputFile);
            }
            else
            {
                // Otherwise, create a new output container
                outputFile                       = new OutputFile();
                outputFile.File                  = filePath;
                outputFile.Project               = GetFullProjectPath(output);
                outputFile.Encoding              = output.Encoding;
                outputFile.BuildAction           = output.BuildAction;
                outputFile.CustomTool            = output.CustomTool;
                outputFile.CustomToolNamespace   = output.CustomToolNamespace;
                outputFile.CopyToOutputDirectory = output.CopyToOutputDirectory;
                outputFile.PreserveExistingFile  = output.PreserveExistingFile;
                this.outputFiles.Add(outputFile);
            }

            outputFile.Content.Append(content);
            outputFile.AppendBuildProperties(output.BuildProperties);
            outputFile.AppendReferences(output.References);
        }
Esempio n. 3
0
        /// <summary>
        /// Finds project item collection for the output file in Visual Studio solution.
        /// </summary>
        /// <param name="output">
        /// An <see cref="OutputFile"/> that needs to be added to the solution.
        /// </param>
        /// <param name="projects">
        /// All <see cref="Project"/>s in the current <see cref="Solution"/>.
        /// </param>
        /// <param name="template">
        /// A <see cref="ProjectItem"/> that represents T4 template being transformed.
        /// </param>
        /// <returns>A <see cref="ProjectItems"/> collection where the generated file should be added.</returns>
        private static ProjectItems FindProjectItemCollection(OutputFile output, IEnumerable <Project> projects, ProjectItem template)
        {
            ProjectItems collection;   // collection to which output file needs to be added
            string       relativePath; // path from the collection to the file
            string       basePath;     // absolute path to the directory to which an item is being added

            if (!string.IsNullOrEmpty(output.Project))
            {
                // If output file needs to be added to another project
                Project project = projects.First(p => OutputInfo.SamePath(GetPath(p), output.Project));
                collection   = project.ProjectItems;
                relativePath = GetRelativePath(GetPath(project), output.File);
                basePath     = Path.GetDirectoryName(GetPath(project));
            }
            else if (output.PreserveExistingFile ||
                     !OutputInfo.SamePath(Path.GetDirectoryName(output.File), Environment.CurrentDirectory))
            {
                // If output file needs to be added to another folder of the current project
                collection   = template.ContainingProject.ProjectItems;
                relativePath = GetRelativePath(GetPath(template.ContainingProject), output.File);
                basePath     = Path.GetDirectoryName(GetPath(template.ContainingProject));
            }
            else
            {
                // Add the output file to the list of children of the template file
                collection   = template.ProjectItems;
                relativePath = GetRelativePath(template.get_FileNames(1), output.File);
                basePath     = Path.GetDirectoryName(template.get_FileNames(1));
            }

            // make sure that all folders in the file path exist in the project.
            if (relativePath.StartsWith("." + Path.DirectorySeparatorChar, StringComparison.Ordinal))
            {
                // Remove leading .\ from the path
                relativePath = relativePath.Substring(relativePath.IndexOf(Path.DirectorySeparatorChar) + 1);

                while (relativePath.Contains(Path.DirectorySeparatorChar))
                {
                    string      folderName = relativePath.Substring(0, relativePath.IndexOf(Path.DirectorySeparatorChar));
                    ProjectItem folder     = AddFolder(collection, folderName, basePath);

                    collection   = folder.ProjectItems;
                    relativePath = relativePath.Substring(folderName.Length + 1);
                    basePath     = Path.Combine(basePath, folderName);
                }
            }

            return(collection);
        }
Esempio n. 4
0
        /// <summary>
        /// Validates that <paramref name="output"/> properties are consistent
        /// with properties of the <paramref name="previousOutput"/>.
        /// </summary>
        /// <param name="output">
        /// An <see cref="OutputInfo"/> object that represents the generated output content.
        /// </param>
        /// <param name="previousOutput">
        /// An <see cref="OutputInfo"/> object that represents previously generated output content.
        /// </param>
        private static void Validate(OutputInfo output, OutputInfo previousOutput)
        {
            if (!OutputInfo.SamePath(previousOutput.Project, GetFullProjectPath(output)))
            {
                throw new TransformationException("Project name doesn't match previously generated value");
            }

            if (previousOutput.Encoding != output.Encoding)
            {
                throw new TransformationException("Encoding doesn't match previously generated value");
            }

            if (previousOutput.BuildAction != output.BuildAction)
            {
                throw new TransformationException("Build action doesn't match previously generated value");
            }

            if (previousOutput.CopyToOutputDirectory != output.CopyToOutputDirectory)
            {
                throw new TransformationException("CopyToOutputDirectory doesn't match previously generated value");
            }

            if (previousOutput.CustomTool != output.CustomTool)
            {
                throw new TransformationException("CustomTool doesn't match previously generated value");
            }

            if (previousOutput.CustomToolNamespace != output.CustomToolNamespace)
            {
                throw new TransformationException("CustomToolNamespace doesn't match previously generated value");
            }

            foreach (KeyValuePair <string, string> buildProperty in previousOutput.BuildProperties)
            {
                string previousValue;
                bool   propertyExists = output.BuildProperties.TryGetValue(buildProperty.Key, out previousValue);
                if (propertyExists && buildProperty.Value != previousValue)
                {
                    throw new TransformationException("Build property doesn't match previously generated value");
                }
            }

            if (previousOutput.PreserveExistingFile != output.PreserveExistingFile)
            {
                throw new TransformationException("PreserveExistingFile doesn't match previously generated value");
            }
        }
        /// <summary>
        /// Performs validation tasks that require accessing Visual Studio automation model.
        /// </summary>
        /// <param name="outputFiles">
        /// <see cref="OutputFile"/>s that need to be added to the solution.
        /// </param>
        /// <param name="projects">
        /// All <see cref="Project"/>s in the current solution.
        /// </param>
        /// <remarks>
        /// Most of the output validation is done on the fly, by the <see cref="OutputManager.Append"/>
        /// method. This method performs the remaining validations that access Visual
        /// Studio automation model and cannot cross <see cref="bAppDomain"/> boundries.
        /// </remarks>
        private static void Validate(IEnumerable <OutputFile> outputFiles, IEnumerable <Project> projects)
        {
            foreach (OutputFile outputFile in outputFiles)
            {
                if (string.IsNullOrEmpty(outputFile.Project))
                {
                    continue;
                }

                // Make sure that project is included in the solution
                bool projectInSolution = projects.Any(p => OutputInfo.SamePath(GetPath(p), outputFile.Project));
                if (!projectInSolution)
                {
                    throw new TransformationException(
                              string.Format(CultureInfo.CurrentCulture, "Target project {0} does not belong to the solution", outputFile.Project));
                }
            }
        }
        /// <summary>
        /// Deletes output files that were not generated by the current session.
        /// </summary>
        /// <param name="outputFiles">
        /// A read-only collection of <see cref="OutputFile"/> objects.
        /// </param>
        /// <param name="solution">
        /// Current Visual Studio <see cref="Solution"/>.
        /// </param>
        /// <param name="template">
        /// A <see cref="ProjectItem"/> that represents T4 template being transformed.
        /// </param>
        private static void DeleteOldOutputs(IEnumerable <OutputFile> outputFiles, Solution solution, ProjectItem template)
        {
            // If previous transformation produced a log of generated ouptut files
            string logFile = GetLogFileName();

            if (File.Exists(logFile))
            {
                // Delete all files recorded in the log that were not regenerated
                foreach (string line in File.ReadAllLines(logFile))
                {
                    string relativePath = line.Trim();

                    // Skip blank lines
                    if (relativePath.Length == 0)
                    {
                        continue;
                    }

                    // Skip comments
                    if (relativePath.StartsWith("//", StringComparison.OrdinalIgnoreCase))
                    {
                        continue;
                    }

                    string absolutePath = Path.GetFullPath(relativePath);

                    // Skip the file if it was regenerated during current transformation
                    if (outputFiles.Any(output => OutputInfo.SamePath(output.File, absolutePath)))
                    {
                        continue;
                    }

                    // The file wasn't regenerated, delete it from the solution, source control and file storage
                    ProjectItem projectItem = solution.FindProjectItem(absolutePath);
                    if (projectItem != null)
                    {
                        DeleteProjectItem(projectItem);
                    }
                }
            }

            // Also delete all project items nested under template if they weren't regenerated
            string templateFileName = Path.GetFileNameWithoutExtension(Host.TemplateFile);

            foreach (ProjectItem childProjectItem in template.ProjectItems)
            {
                // Skip the file if it has the same name as the template file. This will prevent constant
                // deletion and adding of the main output file to the project, which is slow and may require
                // the user to check the file out unnecessarily.
                if (templateFileName == Path.GetFileNameWithoutExtension(childProjectItem.Name))
                {
                    continue;
                }

                // If the file wan't regenerated, delete it from the the solution, source control and file storage
                if (!outputFiles.Any(o => OutputInfo.SamePath(o.File, childProjectItem.get_FileNames(1))))
                {
                    childProjectItem.Delete();
                }
            }
        }