core.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. '''
  2. @author: olivier.massot
  3. '''
  4. import re
  5. from path import Path
  6. objects = []
  7. # TODO: gérer les cas où des objets de types différents portent le même nom. Ex: une table et un formulaire...
  8. # TODO: ignorer les commentaires dans le modules
  9. # TODO: ignorer les labels dans les formulaires et états
  10. # NB: une requete et une table ne peuvent pas porter le même nom.
  11. RXS = {}
  12. def getrx(nom):
  13. try:
  14. return RXS[nom]
  15. except KeyError:
  16. rx = re.compile(r"(?:^|\W)({})(?:$|\W)".format(nom))
  17. RXS[nom] = rx
  18. return rx
  19. def recurse(acc_obj):
  20. deptree = []
  21. for dep in acc_obj.deps:
  22. deptree.append(dep)
  23. if dep.deps:
  24. deptree += recurse(dep)
  25. return deptree
  26. class AccessObject():
  27. type_ = "<unknown>"
  28. def __init__(self, nom, sourcefile):
  29. self.nom = nom
  30. self.functions = []
  31. self.sourcefile = sourcefile
  32. self.deps = []
  33. self.refs = []
  34. def __repr__(self):
  35. return "<{}: {}>".format(self.type_, self.nom)
  36. @classmethod
  37. def from_file(cls, file):
  38. return cls(AccessObject.path_to_name(file), file)
  39. def add_dep(self, obj):
  40. if not obj in self.deps:
  41. self.deps.append(obj)
  42. def add_ref(self, obj):
  43. if not obj in self.refs:
  44. self.refs.append(obj)
  45. SUBSTR = {92: "\\", 47: "/", 58: ":", 42: "*", 63:"?", 34:"\"", 60:"<", 62:">", 124:"|" }
  46. @staticmethod
  47. def path_to_name(path):
  48. name_ = path.name.stripext()
  49. for ascii_code, char in AccessObject.SUBSTR.items():
  50. name_ = name_.replace("[{}]".format(ascii_code), char)
  51. return name_
  52. class TableObject(AccessObject):
  53. type_ = "Table"
  54. class QueryObject(AccessObject):
  55. type_ = "Query"
  56. class FormObject(AccessObject):
  57. type_ = "Form"
  58. class ReportObject(AccessObject):
  59. type_ = "Report"
  60. class MacroObject(AccessObject):
  61. type_ = "Macro"
  62. class ModuleObject(AccessObject):
  63. type_ = "Module"
  64. class RelationObject(AccessObject):
  65. type_ = "Relation"
  66. REFS_ONLY = 1
  67. DEPS_ONLY = 2
  68. DEPS_AND_REFS = 3
  69. class Analyse():
  70. objects = []
  71. duplicated_names = []
  72. @staticmethod
  73. def report(current, total, msg=""):
  74. pass
  75. @staticmethod
  76. def ended():
  77. pass
  78. @classmethod
  79. def register(cls, obj):
  80. if obj.nom in [other.nom for other in cls.objects] and not obj.nom in cls.duplicated_names:
  81. cls.duplicated_names.append(obj.nom)
  82. cls.objects.append(obj)
  83. @classmethod
  84. def run(cls, source_dir, mode=DEPS_AND_REFS):
  85. source_dir = Path(source_dir)
  86. cls.objects = []
  87. cls.duplicated_names = []
  88. # Liste les objets à partir de l'arborescence du repertoire des sources
  89. cls.report(0, 100, "Analyse du répertoire")
  90. for file in Path(source_dir / "forms").files("*.bas"):
  91. obj = FormObject.from_file(file)
  92. cls.register(obj)
  93. for file in Path(source_dir / "reports").files("*.bas"):
  94. obj = ReportObject.from_file(file)
  95. cls.register(obj)
  96. for file in Path(source_dir / "relations").files("*.bas"):
  97. obj = RelationObject.from_file(file)
  98. cls.register(obj)
  99. for file in Path(source_dir / "scripts").files("*.bas"):
  100. obj = MacroObject.from_file(file)
  101. cls.register(obj)
  102. for file in Path(source_dir / "queries").files("*.bas"):
  103. obj = QueryObject.from_file(file)
  104. cls.register(obj)
  105. for file in Path(source_dir / "tables").files("*.xml") + Path(source_dir / "tables").files("*.lnkd"):
  106. obj = TableObject.from_file(file)
  107. cls.register(obj)
  108. rx = re.compile(r"Sub|Function ([^(]+)\(")
  109. for file in Path(source_dir / "modules").files("*.bas"):
  110. obj = ModuleObject.from_file(file)
  111. obj.functions = [fname for fname in rx.findall(file.text()) if fname]
  112. cls.register(obj)
  113. total = len(cls.objects)
  114. cls.report(0, total, "> {} objets trouvés".format(total))
  115. # met à jour les dependances en listant les noms d'objets mentionnés dans le fichier subject
  116. for i, subject in enumerate(cls.objects):
  117. cls.report(i, total, "* {}: {}".format(subject.type_, subject.nom))
  118. source = subject.sourcefile.text()
  119. for object_ in cls.objects:
  120. if object_ is subject:
  121. continue
  122. if getrx(object_.nom).search(source):
  123. if mode in (DEPS_AND_REFS, DEPS_ONLY):
  124. subject.add_dep(object_)
  125. if mode in (DEPS_AND_REFS, REFS_ONLY):
  126. object_.add_ref(subject)
  127. continue
  128. for fname in object_.functions:
  129. if getrx(fname).search(source):
  130. if mode in (DEPS_AND_REFS, DEPS_ONLY):
  131. subject.add_dep(object_)
  132. if mode in (DEPS_AND_REFS, REFS_ONLY):
  133. object_.add_ref(subject)
  134. break
  135. cls.report(total, total, "Analyse terminée")
  136. cls.ended()
  137. return cls.objects
  138. if __name__ == "__main__":
  139. source_dir = Path(r"c:\dev\access\Analytique\source")
  140. here = Path(__file__).parent.abspath()
  141. datafile = here / "access_data.txt"
  142. datafile.remove_p()
  143. def main_report(*_, msg=""):
  144. print(msg)
  145. Analyse.report = main_report
  146. Analyse.run(source_dir)
  147. with open("data.txt", "w+") as f:
  148. for o in Analyse.objects:
  149. f.write("* {} - {}{}\n\tdeps > {}\n".format(o.type_,
  150. o.nom,
  151. "\n\tfunctions > ".format(", ".join(o.functions)) if o.functions else "",
  152. o.deps))
  153. print("# Terminé")