コード例 #1
0
ファイル: Target.cs プロジェクト: loafbrad/NaturalDocs
 /* Function: FinishedPurging
  * Call this after you're done calling all the other purging functions.  Pass the reference to the bool that tracked whether
  * we've registered a PossiblyLongOperation for purging with <Engine.Instance>.  If it's set to true it will call
  * <Engine.Instance.EndPossiblyLongOperation()> and set it to false.
  */
 protected void FinishedPurging(ref bool inPurgingOperation)
 {
     if (inPurgingOperation)
     {
         EngineInstance.EndPossiblyLongOperation();
         inPurgingOperation = false;
     }
 }
コード例 #2
0
        /* Function: Start
         *
         * Initializes the configuration and returns whether all the settings are correct and that execution is ready to begin.
         * If there are problems they are added as <Errors> to the errorList parameter.  This class is *not* designed to allow
         * multiple attempts.  If this function fails scrap the entire <Engine.Instance> and start again.
         *
         * After <Start()> is called the properties of this class become read-only.  This function will add all the input and filter
         * targets it has to <Files.Manager>, and all output targets to <Output.Manager>.
         */
        public bool Start(ErrorList errorList, ProjectConfig commandLineConfig)
        {
            bool success = true;


            // Validate project config folder

            projectConfigFolder = commandLineConfig.ProjectConfigFolder;

            if (!commandLineConfig.ProjectConfigFolderPropertyLocation.IsDefined ||
                String.IsNullOrEmpty(projectConfigFolder))
            {
                errorList.Add(
                    message: Locale.Get("NaturalDocs.Engine", "Error.NoProjectConfigFolder"),
                    configSource: Source.CommandLine,
                    property: "ProjectConfigFolder"
                    );

                success = false;
            }

            else if (!System.IO.Directory.Exists(projectConfigFolder))
            {
                errorList.Add(
                    message: Locale.Get("NaturalDocs.Engine", "Error.ProjectConfigFolderDoesntExist(name)", projectConfigFolder),
                    propertyLocation: commandLineConfig.ProjectConfigFolderPropertyLocation,
                    property: "ProjectConfigFolder"
                    );

                success = false;
            }

            else if (projectConfigFolder == SystemConfigFolder)
            {
                errorList.Add(
                    message: Locale.Get("NaturalDocs.Engine", "Error.ProjectConfigFolderCannotEqualSystemConfigFolder"),
                    propertyLocation: commandLineConfig.ProjectConfigFolderPropertyLocation,
                    property: "ProjectConfigFolder"
                    );

                success = false;
            }

            if (success == false)
            {
                return(false);
            }


            // Load and merge configuration files

            ProjectConfig combinedConfig = new ProjectConfig(Source.Combined);

            MergeConfig(combinedConfig, commandLineConfig);

            var projectTxtParser = new Project_txt();

            if (System.IO.File.Exists(projectConfigFolder + "/Project.txt"))
            {
                ProjectConfig projectTxtConfig;

                if (projectTxtParser.Load(projectConfigFolder + "/Project.txt", out projectTxtConfig, errorList))
                {
                    MergeConfig(combinedConfig, projectTxtConfig);
                }
                else
                {
                    success = false;
                }
            }
            else if (System.IO.File.Exists(ProjectConfigFolder + "/Menu.txt"))
            {
                // Try to extract information from a pre-2.0 Menu.txt instead.

                ProjectConfig menuTxtConfig;
                var           menuTxtParser = new Menu_txt();

                if (menuTxtParser.Load(ProjectConfigFolder + "/Menu.txt", out menuTxtConfig))
                {
                    MergeConfig(combinedConfig, menuTxtConfig);
                }
                // No errors if this fails
            }
            // If neither file exists it's not an error condition.  Just treat it as if nothing was defined.

            MergeConfig(combinedConfig, systemDefaultConfig);

            if (success == false)
            {
                return(false);
            }


            // Validate the working data folder, creating one if it doesn't exist.

            workingDataFolder = combinedConfig.WorkingDataFolder;

            if (!combinedConfig.WorkingDataFolderPropertyLocation.IsDefined ||
                String.IsNullOrEmpty(workingDataFolder))
            {
                workingDataFolder = projectConfigFolder + "/Working Data";
            }

            if (!System.IO.Directory.Exists(workingDataFolder))
            {
                try
                { System.IO.Directory.CreateDirectory(workingDataFolder); }
                catch
                {
                    errorList.Add(
                        message: Locale.Get("NaturalDocs.Engine", "Error.CantCreateWorkingDataFolder(name)", workingDataFolder),
                        property: "WorkingDataFolder"
                        );

                    success = false;
                }
            }

            if (success == false)
            {
                return(false);
            }


            // Load the previous configuration state.  Remember that not every value in ProjectConfig is stored in Project.nd.

            ProjectConfig previousConfig  = null;
            var           projectNDParser = new Project_nd();

            if (ReparseEverything == false && System.IO.File.Exists(workingDataFolder + "/Project.nd"))
            {
                if (!projectNDParser.Load(workingDataFolder + "/Project.nd", out previousConfig))
                {
                    previousConfig = null;
                }
            }


            // Merge output target numbers from Project.nd into the settings.  These are not stored in Project.txt because they're
            // pretty expendible.

            if (previousConfig != null)
            {
                foreach (var target in combinedConfig.OutputTargets)
                {
                    foreach (var previousTarget in previousConfig.OutputTargets)
                    {
                        if (target.IsSameTarget(previousTarget))
                        {
                            target.Number = previousTarget.Number;
                            break;
                        }
                    }
                }
            }


            // Target validation

            if (combinedConfig.InputTargets.Count < 1)
            {
                errorList.Add(
                    message: Locale.Get("NaturalDocs.Engine", "Error.NoInputTargets"),
                    property: "InputTargets"
                    );
                success = false;
            }
            if (combinedConfig.OutputTargets.Count < 1)
            {
                errorList.Add(
                    message: Locale.Get("NaturalDocs.Engine", "Error.NoOutputTargets"),
                    property: "OutputTargets"
                    );
                success = false;
            }

            for (int i = 0; i < combinedConfig.InputTargets.Count; i++)
            {
                if (combinedConfig.InputTargets[i].Validate(errorList, i) == false)
                {
                    success = false;
                }
            }
            for (int i = 0; i < combinedConfig.FilterTargets.Count; i++)
            {
                if (combinedConfig.FilterTargets[i].Validate(errorList, i) == false)
                {
                    success = false;
                }
            }
            for (int i = 0; i < combinedConfig.OutputTargets.Count; i++)
            {
                if (combinedConfig.OutputTargets[i].Validate(errorList, i) == false)
                {
                    success = false;
                }
            }

            if (success == false)
            {
                return(false);
            }


            // Determine the target numbers that are already used and reset duplicates.

            IDObjects.NumberSet usedSourceNumbers = new IDObjects.NumberSet();
            IDObjects.NumberSet usedImageNumbers  = new IDObjects.NumberSet();

            foreach (var target in combinedConfig.InputTargets)
            {
                if (target.Number != 0)
                {
                    if (target.Type == Files.InputType.Source)
                    {
                        if (usedSourceNumbers.Contains(target.Number))
                        {
                            target.Number = 0;
                            target.NumberPropertyLocation = Source.NotDefined;
                        }
                        else
                        {
                            usedSourceNumbers.Add(target.Number);
                        }
                    }

                    else if (target.Type == Files.InputType.Image)
                    {
                        if (usedImageNumbers.Contains(target.Number))
                        {
                            target.Number = 0;
                            target.NumberPropertyLocation = Source.NotDefined;
                        }
                        else
                        {
                            usedImageNumbers.Add(target.Number);
                        }
                    }
                }
            }


            IDObjects.NumberSet usedOutputNumbers    = new IDObjects.NumberSet();
            IDObjects.NumberSet outputNumbersToPurge = new IDObjects.NumberSet();

            foreach (var target in combinedConfig.OutputTargets)
            {
                if (target.Number != 0)
                {
                    if (usedOutputNumbers.Contains(target.Number))
                    {
                        target.Number = 0;
                        target.NumberPropertyLocation = Source.NotDefined;

                        // Since we don't know which of the two entries generated working data under this number, purge it to be safe.
                        outputNumbersToPurge.Add(target.Number);
                    }
                    else
                    {
                        usedOutputNumbers.Add(target.Number);
                    }
                }
            }


            // Assign numbers to the entries that don't already have them and generate default input folder names.

            foreach (var target in combinedConfig.InputTargets)
            {
                if (target.Type == Files.InputType.Source)
                {
                    if (target.Number == 0)
                    {
                        target.Number = usedSourceNumbers.LowestAvailable;
                        target.NumberPropertyLocation = Source.SystemGenerated;

                        usedSourceNumbers.Add(target.Number);
                    }

                    if (target.Name == null && combinedConfig.InputTargets.Count > 1)
                    {
                        target.GenerateDefaultName();
                    }
                }

                else if (target.Type == Files.InputType.Image)
                {
                    if (target.Number == 0)
                    {
                        target.Number = usedImageNumbers.LowestAvailable;
                        target.NumberPropertyLocation = Source.SystemGenerated;

                        usedImageNumbers.Add(target.Number);
                    }
                }
            }

            foreach (var target in combinedConfig.OutputTargets)
            {
                if (target.Number == 0)
                {
                    target.Number = usedOutputNumbers.LowestAvailable;
                    target.NumberPropertyLocation = Source.SystemGenerated;

                    usedOutputNumbers.Add(target.Number);

                    // If we're assigning it for the first time, purge it on the off chance that there's data left over from another
                    // builder.
                    outputNumbersToPurge.Add(target.Number);
                }
            }


            // Rebuild everything if there's an output target that didn't exist on the last run.

            foreach (var target in combinedConfig.OutputTargets)
            {
                bool foundMatch = false;

                if (previousConfig != null)
                {
                    foreach (var previousTarget in previousConfig.OutputTargets)
                    {
                        if (previousTarget.IsSameTarget(target))
                        {
                            foundMatch = true;
                            break;
                        }
                    }
                }

                if (foundMatch == false)
                {
                    RebuildAllOutput = true;
                    break;
                }
            }


            // Apply global settings.

            // DEPENDENCY: We assume all these settings are set in systemDefaultConfig so we don't have to worry about them being
            // undefined.
            tabWidth       = combinedConfig.TabWidth;
            documentedOnly = combinedConfig.DocumentedOnly;
            autoGroup      = combinedConfig.AutoGroup;
            shrinkFiles    = combinedConfig.ShrinkFiles;

            if (previousConfig == null ||
                tabWidth != previousConfig.TabWidth ||
                documentedOnly != previousConfig.DocumentedOnly ||
                autoGroup != previousConfig.AutoGroup)
            {
                ReparseEverything = true;
            }
            else if (previousConfig != null &&
                     shrinkFiles != previousConfig.ShrinkFiles)
            {
                RebuildAllOutput = true;
            }


            // Resave the configuration. Project_txt will handle skipping all the default and command line properties.

            projectTxtParser.Save(projectConfigFolder + "/Project.txt", combinedConfig, errorList);
            projectNDParser.Save(workingDataFolder + "/Project.nd", combinedConfig);


            // Create file sources and filters for Files.Manager

            foreach (var target in combinedConfig.InputTargets)
            {
                EngineInstance.Files.AddFileSource(CreateFileSource(target));
            }

            foreach (var target in combinedConfig.FilterTargets)
            {
                EngineInstance.Files.AddFilter(CreateFilter(target));
            }

            // Some people may put the output folder in their source folder.  Exclude it automatically.
            foreach (var target in combinedConfig.OutputTargets)
            {
                var filter = CreateOutputFilter(target);

                if (filter != null)
                {
                    EngineInstance.Files.AddFilter(filter);
                }
            }


            // Create more default filters

            EngineInstance.Files.AddFilter(new Engine.Files.Filters.IgnoredSourceFolder(ProjectConfigFolder));
            EngineInstance.Files.AddFilter(new Engine.Files.Filters.IgnoredSourceFolder(WorkingDataFolder));
            EngineInstance.Files.AddFilter(new Engine.Files.Filters.IgnoredSourceFolder(SystemConfigFolder));
            EngineInstance.Files.AddFilter(new Engine.Files.Filters.IgnoredSourceFolder(SystemStyleFolder));

            EngineInstance.Files.AddFilter(new Engine.Files.Filters.IgnoredSourceFolderRegex(new Regex.Config.DefaultIgnoredSourceFolderRegex()));


            // Check all input folder entries against the filters.

            for (int i = 0; i < combinedConfig.InputTargets.Count; i++)
            {
                if (combinedConfig.InputTargets[i] is Targets.SourceFolder)
                {
                    var sourceFolderTarget = (Targets.SourceFolder)combinedConfig.InputTargets[i];

                    if (sourceFolderTarget.Type == Files.InputType.Source &&
                        EngineInstance.Files.SourceFolderIsIgnored(sourceFolderTarget.Folder))
                    {
                        errorList.Add(
                            message: Locale.Get("NaturalDocs.Engine", "Error.SourceFolderIsIgnored(sourceFolder)", sourceFolderTarget.Folder),
                            propertyLocation: sourceFolderTarget.FolderPropertyLocation,
                            property: "InputTargets[" + i + "].Folder"
                            );

                        success = false;
                    }
                }
                else
                {
                    throw new NotImplementedException();
                }
            }


            // Create builders for Output.Manager

            foreach (var target in combinedConfig.OutputTargets)
            {
                // Merge the global project info so it has a complete configuration.  The configuration files have already been saved without it.
                MergeProjectInfo(target.ProjectInfo, combinedConfig.ProjectInfo);

                EngineInstance.Output.AddBuilder(CreateBuilder(target));
            }


            // Purge stray output working data, since otherwise it will be left behind if an output entry is removed.

            Regex.Config.OutputPathNumber outputPathNumberRegex = new Regex.Config.OutputPathNumber();
            bool raisedPossiblyLongOperationEvent = false;

            string[] outputDataFolders = System.IO.Directory.GetDirectories(workingDataFolder, "Output*", System.IO.SearchOption.TopDirectoryOnly);
            foreach (string outputDataFolder in outputDataFolders)
            {
                System.Text.RegularExpressions.Match match = outputPathNumberRegex.Match(outputDataFolder);
                if (match.Success)
                {
                    string numberString = match.Groups[1].ToString();
                    int    number;

                    if (String.IsNullOrEmpty(numberString))
                    {
                        number = 1;
                    }
                    else
                    {
                        number = int.Parse(numberString);
                    }

                    if (outputNumbersToPurge.Contains(number) || !usedOutputNumbers.Contains(number))
                    {
                        // Since we're deleting an entire folder, mark it as a possibly long operation.  Some output formats may create many
                        // files in there which could take a while to clear out.
                        if (!raisedPossiblyLongOperationEvent)
                        {
                            EngineInstance.StartPossiblyLongOperation("PurgingOutputWorkingData");
                            raisedPossiblyLongOperationEvent = true;
                        }

                        try
                        { System.IO.Directory.Delete(outputDataFolder, true); }
                        catch (Exception e)
                        {
                            if (!(e is System.IO.IOException || e is System.IO.DirectoryNotFoundException))
                            {
                                throw;
                            }
                        }
                    }
                }
            }

            string[] outputDataFiles = System.IO.Directory.GetFiles(workingDataFolder, "Output*.nd", System.IO.SearchOption.TopDirectoryOnly);
            foreach (string outputDataFile in outputDataFiles)
            {
                System.Text.RegularExpressions.Match match = outputPathNumberRegex.Match(outputDataFile);
                if (match.Success)
                {
                    string numberString = match.Groups[1].ToString();
                    int    number;

                    if (String.IsNullOrEmpty(numberString))
                    {
                        number = 1;
                    }
                    else
                    {
                        number = int.Parse(numberString);
                    }

                    if (outputNumbersToPurge.Contains(number) || !usedOutputNumbers.Contains(number))
                    {
                        // Since this should just be a few individual files we don't have to worry about it being a possibly long operation,
                        // although this will piggyback on that event if it was already raised.

                        System.IO.File.Delete(outputDataFile);
                    }
                }
            }

            if (raisedPossiblyLongOperationEvent)
            {
                EngineInstance.EndPossiblyLongOperation();
            }


            return(success);
        }