User:Bugman/subsystems: Difference between revisions

From FlightGear wiki
Jump to navigation Jump to search
m (Sectioning.)
(→‎Script: Newest version of the script.)
Line 7: Line 7:
{{collapsible script
{{collapsible script
| type  = Python script
| type  = Python script
| title  = Python script for finding all subsystems within the C++ code base.
| title  = Python script for finding all subsystems within the flightgear and simgear C++ code bases.
| intro  = This script requires the ''grep'' Unix command, which can be installed on any OS.  And it must be run with Python3.
| lang  = python
| lang  = python
| script =  
| script =  
#! /usr/bin/env python3
#! /usr/bin/env python3


 
# Python module imports.
import operator
import operator
from re import search, split
from re import search, split
from subprocess import PIPE, Popen
from subprocess import PIPE, Popen
import sys
# Source code repository paths.
SIMGEAR_PATH = "/flightgear/src/flightgear-simgear"
FLIGHTGEAR_PATH = "/flightgear/src/flightgear-flightgear"




class FindSubsystems:
class FindSubsystems:
    """Class for finding all subsystems and subsystem groups."""
     def __init__(self):
     def __init__(self):
        """Find all subsystems and subsystem groups."""
        # SGSubsystem storage lists.
        self.subsystems = [[], [], [], []]
        self.groups = [[], [], [], []]
        # The base objects.
        subsystem_base = Subsystem("SGSubsystem")
        group_base = Subsystem("SGSubsystemGroup", base_class=Subsystem("SGSubsystem"))
        # Add some problematic non-parsable classes.
        self.subsystems[1].append(Subsystem("FGAISim", base_class=Subsystem("FGInterface", base_class=Subsystem("SGSubsystem")), file_name="src/FDM/SP/AISim.hpp"))
        # Find all SGSubsystem and SGSubsystemGroup derived classes.
        paths = [SIMGEAR_PATH, FLIGHTGEAR_PATH]
        printout = [False, True]
        for i in range(len(paths)):
            self.find_primary(path=paths[i], text="classes", primary=self.subsystems[0], base_name="SGSubsystem", base=subsystem_base, skip=["SGSubsystemGroup"], printout=printout[i])
            self.find_primary(path=paths[i], text="groups", primary=self.groups[0], base_name="SGSubsystemGroup", base=group_base, printout=printout[i])
            self.find_secondary(path=paths[i], text="classes", primary=self.subsystems[0], secondary=self.subsystems[1], printout=printout[i])
            self.find_secondary(path=paths[i], text="groups", primary=self.groups[0], secondary=self.groups[1], printout=printout[i])
            self.find_tertiary(path=paths[i], text="classes", secondary=self.subsystems[1], tertiary=self.subsystems[2], printout=printout[i])
            self.find_tertiary(path=paths[i], text="groups", secondary=self.groups[1], tertiary=self.groups[2], printout=printout[i])
            self.find_quaternary(path=paths[i], text="classes", tertiary=self.subsystems[2], quaternary=self.subsystems[3], printout=printout[i])
            self.find_quaternary(path=paths[i], text="groups", tertiary=self.groups[2], quaternary=self.groups[3], printout=printout[i])
        # Final summary.
        print("\nTotal: %i subsystem classes." % len(self.subsystems[0] + self.subsystems[1] + self.subsystems[2] + self.subsystems[3]))
        print("Total: %i subsystem groups." % len(self.groups[0] + self.groups[1] + self.groups[2] + self.groups[3]))
    def count(self, storage_list):
        """Count the number of subsystems and subsystem groups.
        @param storage_list:    The data structure to count.
        @type storage_list:    list of Subsystem instances
        @return:                The number of subsystems and number of groups.
        @rtype:                int, int
        """


         # Init.
         # Init.
         self.classes_primary = []
         subsystems = 0
         self.classes_secondary = []
         groups = 0
         self.classes_tertiary = []
         for element in storage_list:
         self.classes_quaternary = []
            if element.is_group():
                groups += 1
            else:
                subsystems += 1
 
         # Return the counts.
        return subsystems, groups


        self.find_primary_classes()
        self.find_secondary_classes()
        self.find_tertiary_classes()
        self.find_quaternary_classes()


        self.classes_primary.sort(key=operator.attrgetter('name'))
    def find_primary(self, path=None, text=None, primary=None, base_name=None, base=None, skip=[], printout=False):
        self.classes_secondary.sort(key=operator.attrgetter('name'))
         """Find all primary subsystems and groups
        self.classes_tertiary.sort(key=operator.attrgetter('name'))
         self.classes_quaternary.sort(key=operator.attrgetter('name'))


         # Print out.
         @keyword path:      The path to the repository to search through.
         print("\nPrimary classes (%i):" % len(self.classes_primary))
        @type path:        str
         for subsystem in self.classes_primary:
        @keyword text:      Text identifying subsystems vs. subsystem groups.
             print("    %s" % subsystem)
        @type text:        str
         if self.classes_secondary:
        @keyword primary:  The primary list of subsystems or groups.
             print("\nSecondary classes (%i):" % len(self.classes_secondary))
        @type primary:      list of Subsystem instances
             for subsystem in self.classes_secondary:
        @keyword base_name: The name of the base class.
        @type base_name:    str
        @keyword base:      The base class object.
        @type base:        Subsystem instance
        @keyword skip:      A list of class names to skip.
        @type skip:        list of str
        @keyword printout:  A flag which if True will activate the printout of subsystems.
         @type printout:    bool
        """
 
        # Find all subsystems or groups.
         for file_name, class_name in self.grep(path=path, base_name=base_name):
            if class_name in skip:
                continue
             primary.append(Subsystem(class_name, base_class=base, file_name=file_name))
 
        # Sort the subsystems by name.
        primary.sort(key=operator.attrgetter('name'))
 
        # Printout.
         if printout:
            counts = self.count(primary)
             print("\nPrimary %s (%i subsystems, %i groups):" % (text, counts[0], counts[1]))
             for subsystem in primary:
                 print("    %s" % subsystem)
                 print("    %s" % subsystem)
         if self.classes_tertiary:
 
             print("\nTertiary classes (%i):" % len(self.classes_tertiary))
 
             for subsystem in self.classes_tertiary:
    def find_secondary(self, path=None, text=None, primary=None, secondary=None, printout=False):
         """Find all secondary subsystems and groups
 
        @keyword path:      The path to the repository to search through.
        @type path:        str
        @keyword text:      Text identifying subsystems vs. subsystem groups.
        @type text:        str
        @keyword primary:  The primary list of subsystems or groups.
        @type primary:      list of Subsystem instances
        @keyword secondary: The secondary list of subsystems or groups.
        @type secondary:    list of Subsystem instances
        @keyword printout:  A flag which if True will activate the printout of subsystems.
        @type printout:    bool
        """
 
        # Loop over all primary subsystems.
        for subsystem in primary:
            for file_name, derived_class in self.grep(path=path, base_name=subsystem.name):
                secondary.append(Subsystem(derived_class, base_class=subsystem, file_name=file_name))
 
        # Sort the subsystems by name.
        secondary.sort(key=operator.attrgetter('name'))
 
        # Printout.
        if printout and secondary:
            counts = self.count(secondary)
             print("\nSecondary %s (%i subsystems, %i groups):" % (text, counts[0], counts[1]))
             for subsystem in secondary:
                 print("    %s" % subsystem)
                 print("    %s" % subsystem)
         if self.classes_quaternary:
 
             print("\nQuaternary classes (%i):" % len(self.classes_quaternary))
 
             for subsystem in self.classes_quaternary:
    def find_tertiary(self, path=None, text=None, secondary=None, tertiary=None, printout=False):
         """Find all tertiary subsystems and groups
 
        @keyword path:      The path to the repository to search through.
        @type path:        str
        @keyword text:      Text identifying subsystems vs. subsystem groups.
        @type text:        str
        @keyword secondary: The secondary list of subsystems or groups.
        @type secondary:    list of Subsystem instances
        @keyword tertiary:  The tertiary list of subsystems or groups.
        @type tertiary:    list of Subsystem instances
        @keyword printout:  A flag which if True will activate the printout of subsystems.
        @type printout:    bool
        """
 
        # Loop over all secondary subsystems.
        for subsystem in secondary:
            for file_name, derived_class in self.grep(path=path, base_name=subsystem.name):
                tertiary.append(Subsystem(derived_class, base_class=subsystem, file_name=file_name))
 
        # Sort all subsystems by name.
        tertiary.sort(key=operator.attrgetter('name'))
 
        # Printout.
        if printout and tertiary:
            counts = self.count(tertiary)
             print("\nTertiary %s (%i subsystems, %i groups):" % (text, counts[0], counts[1]))
             for subsystem in tertiary:
                 print("    %s" % subsystem)
                 print("    %s" % subsystem)
        print("\nTotal: %i subsystem classes." % len(self.classes_primary + self.classes_secondary + self.classes_tertiary + self.classes_quaternary))




     def find_primary_classes(self):
     def find_quaternary(self, path=None, text=None, tertiary=None, quaternary=None, printout=False):
         base = Subsystem("SGSubsystem")
         """Find all tertiary subsystems and groups
        for file_name, class_name in self.grep("SGSubsystem"):
            self.classes_primary.append(Subsystem(class_name, base_class=base, file_name=file_name))


        @keyword path:          The path to the repository to search through.
        @type path:            str
        @keyword text:          Text identifying subsystems vs. subsystem groups.
        @type text:            str
        @keyword tertiary:      The tertiary list of subsystems or groups.
        @type tertiary:        list of Subsystem instances
        @keyword quaternary:    The quaternary list of subsystems or groups.
        @type quaternary:      list of Subsystem instances
        @keyword printout:      A flag which if True will activate the printout of subsystems.
        @type printout:        bool
        """


    def find_secondary_classes(self):
        # Loop over all tertiary subsystems.
         for subsystem in self.classes_primary:
         for subsystem in tertiary:
             for file_name, derived_class in self.grep(subsystem.name):
             for file_name, derived_class in self.grep(path=path, base_name=subsystem.name):
                 self.classes_secondary.append(Subsystem(derived_class, base_class=subsystem, file_name=file_name))
                 quaternary.append(Subsystem(derived_class, base_class=subsystem, file_name=file_name))


        # Sort all subsystems by name.
        quaternary.sort(key=operator.attrgetter('name'))


    def find_tertiary_classes(self):
        # Printout.
         for subsystem in self.classes_secondary:
         if printout and quaternary:
             for file_name, derived_class in self.grep(subsystem.name):
             counts = self.count(quaternary)
                self.classes_tertiary.append(Subsystem(derived_class, base_class=subsystem, file_name=file_name))
            print("\nQuaternary %s (%i subsystems, %i groups):" % (text, counts[0], counts[1]))
            for subsystem in quaternary:
                print("    %s" % subsystem)




     def find_quaternary_classes(self):
     def grep(self, path=None, base_name=None):
         for subsystem in self.classes_tertiary:
         """Generator method for finding all classes derived from the given base name and repository.
            for file_name, derived_class in self.grep(subsystem.name):
                self.classes_quaternary.append(Subsystem(derived_class, base_class=subsystem, file_name=file_name))


        @keyword path:      The path to the repository to search through.
        @type path:        str
        @keyword base_name: The name of the base class.
        @type base_name:    str
        @return:            The source file and the name of the derived class.
        @rtype:            str, str
        """


    def grep(self, base_name):
        # The Unix grep command to run.
         cmd = 'grep -rI "public \<%s\>" {{!}} grep -v "%s::"' % (base_name, base_name)
         cmd = 'cd %s; grep -rI "public \<%s\>" | grep -v "%s::"' % (path, base_name, base_name)
         pipe = Popen(cmd, shell=True, stdout=PIPE)
         pipe = Popen(cmd, shell=True, stdout=PIPE)
        # Read all output.
         for line in pipe.stdout.readlines():
         for line in pipe.stdout.readlines():
            # Convert the byte-array.
             line = line.decode()
             line = line.decode()
            # Skip this script.
             if search("grep ", line):
             if search("grep ", line):
                 continue
                 continue
            # Cannot handle this line!
             if not search("class ", line):
             if not search("class ", line):
                 print("Skipping: %s" % repr(line[:-1]))
                 sys.stderr.write("\nSkipping: %s\n" % repr(line[:-1]))
                 continue
                 continue
            # Split by single colons.
             elements = split("(?<!:):(?!:)", line)
             elements = split("(?<!:):(?!:)", line)
            # The file and class name.
             file_name = elements[0]
             file_name = elements[0]
             class_name = elements[1]
             class_name = elements[1]
             class_name = class_name.replace("class", "")
             class_name = class_name.replace("class", "")
             class_name = class_name.strip()
             class_name = class_name.strip()
            # Generator method.
             yield file_name, class_name
             yield file_name, class_name


Line 100: Line 258:


class Subsystem:
class Subsystem:
    """Object for storing the information for a specific subsystem."""
     def __init__(self, name, base_class=None, file_name=None):
     def __init__(self, name, base_class=None, file_name=None):
        """Set up the object.
        @param name:            The name of the subsystem.
        @type name:            str
        @keyword base_class:    The name of the base class.
        @type base_class:      str
        @keyword file_name:    The name of the file containing the subsystem declaration.
        @type file_name:        str
        """
        # Store the data.
         self.name = name
         self.name = name
         self.base_class = base_class
         self.base_class = base_class
Line 107: Line 278:


     def __repr__(self):
     def __repr__(self):
        """Overwrite the string representation of the object.
        @return:    The string representation.
        @rtype:    str
        """
        # The subsystem name.
         string = "<%s" % self.name
         string = "<%s" % self.name
        # The inheritance chain.
         if self.base_class:
         if self.base_class:
             string += " (from %s" % self.base_class.name
             string += " : %s" % self.base_class.name
             if self.base_class.base_class:
             if self.base_class.base_class:
                 string += " : %s" % self.base_class.base_class.name
                 string += " : %s" % self.base_class.base_class.name
Line 116: Line 296:
                     if self.base_class.base_class.base_class.base_class:
                     if self.base_class.base_class.base_class.base_class:
                         string += " : %s" % self.base_class.base_class.base_class.base_class.name
                         string += " : %s" % self.base_class.base_class.base_class.base_class.name
            string += ")"
                        if self.base_class.base_class.base_class.base_class.base_class:
                            string += " : %s" % self.base_class.base_class.base_class.base_class.base_class.name
 
        # Add the source file name.
        string += " in %s" % repr(self.file_name)
 
        # Closure.
         string += ">"
         string += ">"


        # Return the representation.
         return string
         return string
    def is_group(self):
        """Determine this is a subsystem or subsystem group.
        @return:    True if this is a subsystem group.
        @rtype:    bool
        """
        # Chase the base name as far as possible.
        if self.name == "SGSubsystemGroup":
            return True
        if self.base_class.name == "SGSubsystemGroup":
            return True
        if self.base_class.base_class:
            if self.base_class.base_class.name == "SGSubsystemGroup":
                return True
            if self.base_class.base_class.base_class:
                if self.base_class.base_class.base_class.name == "SGSubsystemGroup":
                    return True
                if self.base_class.base_class.base_class.base_class:
                    if self.base_class.base_class.base_class.base_class.name == "SGSubsystemGroup":
                        return True
                    if self.base_class.base_class.base_class.base_class.base_class:
                        if self.base_class.base_class.base_class.base_class.name.base_class.name == "SGSubsystemGroup":
                            return True
        # A normal subsystem.
        return False






# Instantiate the class if run as a script.
if __name__ == "__main__":
if __name__ == "__main__":
     FindSubsystems()
     FindSubsystems()

Revision as of 10:07, 18 April 2018

Tracking down subsystems

Script

The following script is for finding all FlightGear dependencies:

All subsystems

The result is:

Refactoring

To check that all subsystems on a branch have been updated or refactored: