''' @author: olivier.massot ''' import re from path import Path objects = [] # TODO: gérer les cas où des objets de types différents portent le même nom. Ex: une table et un formulaire... # TODO: ignorer les commentaires dans le modules # TODO: ignorer les labels dans les formulaires et états # NB: une requete et une table ne peuvent pas porter le même nom. RXS = {} def getrx(nom): try: return RXS[nom] except KeyError: rx = re.compile(r"(?:^|\W)({})(?:$|\W)".format(nom)) RXS[nom] = rx return rx def recurse(acc_obj): deptree = [] for dep in acc_obj.deps: deptree.append(dep) if dep.deps: deptree += recurse(dep) return deptree class AccessObject(): type_ = "" def __init__(self, nom, sourcefile): self.nom = nom self.functions = [] self.sourcefile = sourcefile self.deps = [] self.refs = [] def __repr__(self): return "<{}: {}>".format(self.type_, self.nom) @classmethod def from_file(cls, file): return cls(AccessObject.path_to_name(file), file) 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_ class TableObject(AccessObject): type_ = "Table" 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" class RelationObject(AccessObject): type_ = "Relation" 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") for file in Path(source_dir / "forms").files("*.bas"): obj = FormObject.from_file(file) cls.register(obj) for file in Path(source_dir / "reports").files("*.bas"): obj = ReportObject.from_file(file) cls.register(obj) for file in Path(source_dir / "relations").files("*.bas"): obj = RelationObject.from_file(file) cls.register(obj) for file in Path(source_dir / "scripts").files("*.bas"): obj = MacroObject.from_file(file) cls.register(obj) for file in Path(source_dir / "queries").files("*.bas"): obj = QueryObject.from_file(file) cls.register(obj) for file in Path(source_dir / "tables").files("*.xml") + Path(source_dir / "tables").files("*.lnkd"): obj = TableObject.from_file(file) cls.register(obj) rx = re.compile(r"Sub|Function ([^(]+)\(") for file in Path(source_dir / "modules").files("*.bas"): obj = ModuleObject.from_file(file) obj.functions = [fname for fname in rx.findall(file.text()) if fname] cls.register(obj) 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 i, subject in enumerate(cls.objects): cls.report(i, total, "* {}: {}".format(subject.type_, subject.nom)) source = subject.sourcefile.text() for object_ in cls.objects: if object_ is subject: continue if getrx(object_.nom).search(source): if mode in (DEPS_AND_REFS, DEPS_ONLY): subject.add_dep(object_) if mode in (DEPS_AND_REFS, REFS_ONLY): object_.add_ref(subject) continue for fname in object_.functions: if getrx(fname).search(source): if mode in (DEPS_AND_REFS, DEPS_ONLY): subject.add_dep(object_) if mode in (DEPS_AND_REFS, REFS_ONLY): object_.add_ref(subject) break 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() datafile = here / "access_data.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: f.write("* {} - {}{}\n\tdeps > {}\n".format(o.type_, o.nom, "\n\tfunctions > ".format(", ".join(o.functions)) if o.functions else "", o.deps)) print("# Terminé")