| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- '''
- @author: olivier.massot
- '''
- import re
- from path import Path
- # TODO: gérer les cas où des objets de types différents portent le même nom. Ex: une table et un formulaire... Note: une requete et une table ne peuvent pas porter le même nom.
- # TODO: ignorer les commentaires dans le modules
- # TODO: ignorer les labels dans les formulaires et états
- # TODO: Gérer les références circulaires
- # TODO: Stocker un aperçu du contexte de la ou des références dans le code source, pour contrôle ultérieur
- # TODO: Permettre de supprimer / ajouter des réferences
- # TODO: Verifier que la recherche puisse être Case sensitive?
- def recurse(acc_obj):
- deptree = []
- for dep in acc_obj.deps:
- deptree.append(dep)
- if dep.deps:
- deptree += recurse(dep)
- return deptree
- class InvalidFileExt(IOError):
- pass
- class AccessObject():
- type_ = "<unknown>"
- _valid_file_exts = (".bas")
- _re_w_sep = """^|\s|\[|\]|&|\(|\)|\.|!|"|'"""
- def __init__(self, nom):
- self.nom = nom
- self.functions = []
- self.sourcefile = ""
- self.deps = []
- self.refs = []
- self._sourcecode = ""
- def __repr__(self):
- return "<{}: {}>".format(self.type_, self.nom)
- @classmethod
- def from_file(cls, file):
- file = Path(file)
- if file.ext not in cls._valid_file_exts:
- raise InvalidFileExt("Format de fichier d'entrée non valide ({})".format(file.name))
- obj = cls(AccessObject.path_to_name(file))
- obj.sourcefile = file
- obj._sourcecode = file.text()
- return obj
- @property
- def sourcecode(self):
- if not self._sourcecode:
- self._sourcecode = self.sourcefile.text()
- return self._sourcecode
- def add_dep(self, obj):
- if not obj in self.deps:
- self.deps.append(obj)
- def add_ref(self, obj):
- if not obj in self.refs:
- self.refs.append(obj)
- SUBSTR = {92: "\\", 47: "/", 58: ":", 42: "*", 63:"?", 34:"\"", 60:"<", 62:">", 124:"|" }
- @staticmethod
- def path_to_name(path):
- name_ = path.name.stripext()
- for ascii_code, char in AccessObject.SUBSTR.items():
- name_ = name_.replace("[{}]".format(ascii_code), char)
- return name_
- def search_me_regex(self):
- return re.compile(r"""({sep}){nom}({sep})""".format(sep=self._re_w_sep, nom=self.nom))
- def containsRefsTo(self, access_object):
- if access_object is self:
- return False
- if type(access_object) is ModuleObject: # L'objet peut contenir des references aux fonctions de l'objet module
- for fname in access_object.functions:
- rx = re.compile(r"(^|\W)({})($|\W)".format(fname))
- if rx.search(self.sourcecode):
- return True
- rx = access_object.search_me_regex()
- return rx.search(self.sourcecode)
- class TableObject(AccessObject):
- type_ = "Table"
- _valid_file_exts = (".xml", ".lnkd")
- class QueryObject(AccessObject):
- type_ = "Query"
- class FormObject(AccessObject):
- type_ = "Form"
- class ReportObject(AccessObject):
- type_ = "Report"
- class MacroObject(AccessObject):
- type_ = "Macro"
- class ModuleObject(AccessObject):
- type_ = "Module"
- @classmethod
- def from_file(cls, file):
- obj = super(ModuleObject, cls).from_file(file)
- rx = re.compile(r"Sub|Function ([^(]+)\(")
- obj.functions = [fname for fname in rx.findall(file.text()) if fname]
- return obj
- class RelationObject(AccessObject):
- type_ = "Relation"
- _valid_file_exts = (".txt")
- REFS_ONLY = 1
- DEPS_ONLY = 2
- DEPS_AND_REFS = 3
- class Analyse():
- objects = []
- duplicated_names = []
- @staticmethod
- def report(current, total, msg=""):
- pass
- @staticmethod
- def ended():
- pass
- @classmethod
- def register(cls, obj):
- if obj.nom in [other.nom for other in cls.objects] and not obj.nom in cls.duplicated_names:
- cls.duplicated_names.append(obj.nom)
- cls.objects.append(obj)
- @classmethod
- def run(cls, source_dir, mode=DEPS_AND_REFS):
- source_dir = Path(source_dir)
- cls.objects = []
- cls.duplicated_names = []
- # Liste les objets à partir de l'arborescence du repertoire des sources
- cls.report(0, 100, "Analyse du répertoire")
- sourcemap = {
- "forms": FormObject,
- "reports": ReportObject,
- "relations": RelationObject,
- "scripts": MacroObject,
- "queries": QueryObject,
- "tables": TableObject,
- "modules": ModuleObject,
- }
- for dirname, accobj in sourcemap.items():
- for file in Path(source_dir / dirname).files():
- try:
- obj = accobj.from_file(file)
- cls.register(obj)
- except InvalidFileExt:
- print("Ignored unrecognized file: {}".format(file))
- total = len(cls.objects)
- cls.report(0, total, "> {} objets trouvés".format(total))
- # met à jour les dependances en listant les noms d'objets mentionnés dans le fichier subject
- for index, subject in enumerate(cls.objects):
- cls.report(index, total, "* {}: {}".format(subject.type_, subject.nom))
- for candidate in cls.objects[:index] + cls.objects[index + 1:]:
- if subject.containsRefsTo(candidate):
- if mode in (DEPS_AND_REFS, DEPS_ONLY):
- subject.add_dep(candidate)
- if mode in (DEPS_AND_REFS, REFS_ONLY):
- candidate.add_ref(subject)
- cls.report(total, total, "Analyse terminée")
- cls.ended()
- return cls.objects
- if __name__ == "__main__":
- source_dir = Path(r"c:\dev\access\Analytique\source")
- here = Path(__file__).parent.abspath()
- source_dir = here / "source"
- datafile = here / "access_analyser.txt"
- datafile.remove_p()
- def main_report(*_, msg=""):
- print(msg)
- Analyse.report = main_report
- Analyse.run(source_dir)
- with open("data.txt", "w+") as f:
- for o in Analyse.objects:
- msg = "# {}: '{}'".format(o.type_, o.nom)
- msg += "\n\t Utilise: {}".format(", ".join(["'{}'".format(d) for d in o.deps]))
- msg += "\n\t Est utilisé par: {}".format(", ".join(["'{}'".format(d) for d in o.refs]))
- msg += "\n"
- f.write(msg)
- print("# Terminé")
|