/src/tools/semantics/liberty_cluster.e

http://github.com/tybor/Liberty · Specman e · 426 lines · 358 code · 35 blank · 33 comment · 22 complexity · 057e4dca71304f75e8438bbba1db3656 MD5 · raw file

  1. -- This file is part of Liberty Eiffel.
  2. --
  3. -- Liberty Eiffel is free software: you can redistribute it and/or modify
  4. -- it under the terms of the GNU General Public License as published by
  5. -- the Free Software Foundation, version 3 of the License.
  6. --
  7. -- Liberty Eiffel is distributed in the hope that it will be useful,
  8. -- but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. -- GNU General Public License for more details.
  11. --
  12. -- You should have received a copy of the GNU General Public License
  13. -- along with Liberty Eiffel. If not, see <http://www.gnu.org/licenses/>.
  14. --
  15. class LIBERTY_CLUSTER
  16. --
  17. -- Clustering rules:
  18. -- A given directory is a cluster if:
  19. --
  20. -- 1. It contains a cluster.rc file.
  21. -- In that case, that file describes the cluster, giving it a name, a version number, dependencies and
  22. -- default options (assertion level, debug...)
  23. -- If the directory also contains a loadpath.se file then that file is used to describe which directories
  24. -- are part of the cluster itself (see below).
  25. --
  26. -- 2. It contains a loadpath.se file.
  27. -- Each line of the loadpath is a path to either a cluster.rc file, a loadpath.se file, or a directory. In
  28. -- the latter case, the clustering rules apply. If the given directory contains neither a cluster.rc nor a
  29. -- loadpath.se file, the directory is considered to belong to the cluster.
  30. --
  31. insert
  32. HASHABLE
  33. LOGGING
  34. undefine
  35. is_equal
  36. end
  37. create {LIBERTY_UNIVERSE}
  38. make_root
  39. create {LIBERTY_CLUSTER}
  40. make_from_loadpath, make_from_etc
  41. create {LIBERTY_CLASS_DESCRIPTOR}
  42. make_void
  43. feature {ANY}
  44. name: FIXED_STRING
  45. locations: TRAVERSABLE[FIXED_STRING]
  46. depth: INTEGER
  47. feature {ANY}
  48. hash_code: INTEGER
  49. is_equal (other: like Current): BOOLEAN is
  50. do
  51. Result := other.locations.is_equal(locations)
  52. end
  53. location_of (a_class_name: FIXED_STRING): FIXED_STRING is
  54. do
  55. Result := class_names.fast_reference_at(a_class_name)
  56. -- if Result = Void then
  57. -- Result := find(a_class_name).location_of(a_class_name)
  58. -- end
  59. end
  60. has_parent (a_cluster: LIBERTY_CLUSTER): BOOLEAN is
  61. do
  62. Result := a_cluster = Current or else (Current /= root and then parent.has_parent(a_cluster))
  63. end
  64. feature {LIBERTY_UNIVERSE, LIBERTY_TYPE_RESOLVER}
  65. find (a_class_name: FIXED_STRING): LIBERTY_CLUSTER is
  66. local
  67. filename: STRING
  68. new_mark: like find_mark
  69. do
  70. filename := once ""
  71. filename.make_from_string(a_class_name)
  72. filename.to_lower
  73. filename.append(once ".e")
  74. find_mark_counter.increment
  75. new_mark := find_mark_counter.value
  76. Result := find_cluster(a_class_name, filename, new_mark)
  77. if Result = Void and then root /= Current then
  78. Result := root.find_cluster(a_class_name, filename, new_mark)
  79. end
  80. ensure
  81. Result /= Void implies Result.location_of(a_class_name) /= Void
  82. end
  83. feature {LIBERTY_CLUSTER}
  84. find_cluster (a_class_name: FIXED_STRING; a_file_name: STRING; new_mark: like find_mark): LIBERTY_CLUSTER is
  85. local
  86. i: INTEGER
  87. do
  88. if find_mark < new_mark then
  89. find_mark := new_mark
  90. from
  91. i := locations.lower
  92. until
  93. Result /= Void or else i > locations.upper
  94. loop
  95. if find_here(locations.item(i), a_file_name) then
  96. class_names.put(locations.item(i), a_class_name)
  97. Result := Current
  98. end
  99. i := i + 1
  100. end
  101. if Result = Void then
  102. Result := find_child(a_class_name, a_file_name, new_mark)
  103. end
  104. end
  105. end
  106. log_cluster_tree (tab: INTEGER) is
  107. require
  108. tab >= 0
  109. local
  110. info: OUTPUT_STREAM; i: INTEGER
  111. do
  112. info := log.info
  113. from
  114. i := 0
  115. until
  116. i = tab
  117. loop
  118. info.put_string(once " | ")
  119. i := i + 1
  120. end
  121. info.put_string(once " +- (")
  122. info.put_integer(depth)
  123. info.put_string(once ") ")
  124. if logged then
  125. info.put_string(name)
  126. info.put_line(once " ...")
  127. else
  128. info.put_line(name)
  129. children.do_all(agent {LIBERTY_CLUSTER}.log_cluster_tree(tab + 1))
  130. logged := True
  131. end
  132. ensure
  133. logged
  134. end
  135. feature {} -- find
  136. find_here (a_location: FIXED_STRING; a_file_name: STRING): BOOLEAN is
  137. local
  138. here: STRING
  139. do
  140. here := once ""
  141. here.make_from_string(a_location)
  142. dir.connect_to(here)
  143. if dir.is_connected then
  144. from
  145. dir.read_entry
  146. until
  147. Result or else dir.end_of_input
  148. loop
  149. if dir.last_entry.is_equal(a_file_name) then
  150. Result := True
  151. end
  152. dir.read_entry
  153. end
  154. dir.disconnect
  155. end
  156. end
  157. find_child (a_class_name: FIXED_STRING; a_file_name: STRING; new_mark: like find_mark): LIBERTY_CLUSTER is
  158. local
  159. child: LIBERTY_CLUSTER
  160. i: INTEGER
  161. do
  162. from
  163. i := children.lower
  164. until
  165. Result /= Void or else i > children.upper
  166. loop
  167. child := children.item(i)
  168. if child.depth >= depth then
  169. Result := child.find_cluster(a_class_name, a_file_name, new_mark)
  170. end
  171. i := i + 1
  172. end
  173. end
  174. feature {}
  175. make_void is
  176. do
  177. name := "<Void>".intern
  178. create class_names.with_capacity(0)
  179. create {FAST_ARRAY[FIXED_STRING]} locations.with_capacity(0)
  180. depth := -1
  181. end
  182. make_root is
  183. local
  184. c: FAST_ARRAY[LIBERTY_CLUSTER]
  185. etc: LIBERTY_ETC
  186. do
  187. name := "<Root>".intern
  188. create {FAST_ARRAY[FIXED_STRING]} locations.with_capacity(0)
  189. create class_names.with_capacity(7)
  190. root := Current
  191. parent := Current
  192. create c.with_capacity(etc.clusters.count)
  193. children := c
  194. etc.clusters.do_all(agent add_if_root({LIBERTY_ETC_CLUSTER}, c))
  195. log_cluster_tree(0)
  196. ensure
  197. depth = 0
  198. end
  199. add_if_root (a_etc: LIBERTY_ETC_CLUSTER; a_children: FAST_ARRAY[LIBERTY_CLUSTER]) is
  200. require
  201. is_root: root = Current
  202. in_other_words: depth = 0
  203. do
  204. if a_etc.depth = 0 and then a_etc.cluster = Void then
  205. log.trace.put_string(name)
  206. log.trace.put_string(once ": adding root cluster from etc ")
  207. log.trace.put_line(a_etc.name)
  208. a_children.add_last(create {LIBERTY_CLUSTER}.make_from_etc(a_etc, Current, Current))
  209. end
  210. end
  211. make_from_etc (a_etc: LIBERTY_ETC_CLUSTER; a_parent: like parent; a_root: like root) is
  212. require
  213. a_etc /= Void
  214. a_root /= Void
  215. a_etc.cluster = Void
  216. a_root.depth = 0
  217. a_parent /= Void
  218. local
  219. c: FAST_ARRAY[LIBERTY_CLUSTER]
  220. do
  221. depth := a_parent.depth + 1
  222. parent := a_parent
  223. name := a_etc.name
  224. root := a_root
  225. locations := a_etc.locations
  226. create class_names.with_capacity(16)
  227. create c.with_capacity(a_etc.needs.count)
  228. children := c
  229. log.info.put_string(once "Cluster (")
  230. log.info.put_integer(depth)
  231. log.info.put_string(once ") ")
  232. log.info.put_string(name)
  233. log.info.put_string(once ": ")
  234. log.info.put_line(locations.out)
  235. a_etc.set_cluster(Current)
  236. a_etc.needs.do_all(agent add_needs({LIBERTY_ETC_NEEDS}, c, a_root))
  237. ensure
  238. root = a_root
  239. parent = a_parent
  240. depth = a_parent.depth + 1
  241. locations = a_etc.locations
  242. end
  243. add_needs (a_etc: LIBERTY_ETC_NEEDS; a_children: FAST_ARRAY[LIBERTY_CLUSTER]; a_root: like root) is
  244. require
  245. a_root.depth = 0
  246. do
  247. if a_etc.cluster.cluster /= Void then
  248. if not has_parent(a_etc.cluster.cluster) then
  249. a_children.add_last(a_etc.cluster.cluster)
  250. end
  251. else
  252. log.trace.put_string(name)
  253. log.trace.put_string(once ": adding child cluster from etc ")
  254. log.trace.put_line(a_etc.cluster.name)
  255. a_children.add_last(create {LIBERTY_CLUSTER}.make_from_etc(a_etc.cluster, Current, root))
  256. end
  257. end
  258. make_from_loadpath (a_loadpath: STRING; a_parent: like parent; a_root: like root) is
  259. require
  260. a_loadpath /= Void
  261. a_root.depth = 0
  262. a_parent /= Void
  263. local
  264. location_directory: STRING
  265. do
  266. log.warning.put_line(once "Effective clusters should not be created directly from classpath.se anymore! (only master clusters should)")
  267. if not ft.is_file(a_loadpath) then
  268. std_error.put_line("*** Error: not a loadpath: " + a_loadpath)
  269. die_with_code(1)
  270. end
  271. depth := a_parent.depth + 1
  272. parent := a_parent
  273. root := a_root
  274. name := a_loadpath.intern
  275. dir.compute_parent_directory_of(a_loadpath)
  276. if dir.last_entry.is_empty then
  277. location_directory := dir.current_working_directory.out
  278. else
  279. location_directory := dir.last_entry.twin
  280. end
  281. create class_names.with_capacity(16)
  282. log.info.put_string(once "Cluster (")
  283. log.info.put_integer(depth)
  284. log.info.put_string(once ") ")
  285. log.info.put_string(name)
  286. log.info.put_string(once ": ")
  287. log.info.put_line(locations.out)
  288. read_loadpath(a_loadpath, location_directory)
  289. ensure
  290. root = a_root
  291. depth = a_parent.depth + 1
  292. parent = a_parent
  293. end
  294. read_loadpath (a_loadpath, a_location_directory: STRING) is
  295. require
  296. root /= Void
  297. ft.is_file(a_loadpath)
  298. local
  299. loc: FAST_ARRAY[FIXED_STRING]
  300. c: FAST_ARRAY[LIBERTY_CLUSTER]
  301. do
  302. create loc.with_capacity(4)
  303. create c.with_capacity(2)
  304. tfr.connect_to(a_loadpath)
  305. if tfr.is_connected then
  306. from
  307. tfr.read_line
  308. until
  309. tfr.end_of_input
  310. loop
  311. env.substitute(tfr.last_string)
  312. process_loadpath(loc, c, a_location_directory, tfr.last_string)
  313. tfr.read_line
  314. end
  315. env.substitute(tfr.last_string)
  316. process_loadpath(loc, c, a_location_directory, tfr.last_string)
  317. tfr.disconnect
  318. end
  319. locations := loc
  320. children := c
  321. end
  322. process_loadpath (a_locations: FAST_ARRAY[FIXED_STRING]; a_children: FAST_ARRAY[LIBERTY_CLUSTER]; a_location_directory, loadpath_line: STRING) is
  323. require
  324. a_locations /= Void
  325. a_children /= Void
  326. loadpath_line /= Void
  327. root /= Void
  328. local
  329. sublocation: STRING
  330. do
  331. if not loadpath_line.is_empty and then not loadpath_line.has_prefix(once "--") then
  332. sublocation := ""
  333. dir.ensure_system_notation
  334. dir.system_notation.from_notation(loadpath_notation, loadpath_line)
  335. if dir.system_notation.is_absolute_path(loadpath_line) then
  336. sublocation.copy(loadpath_line)
  337. else
  338. dir.compute_subdirectory_with(a_location_directory, loadpath_line)
  339. if dir.last_entry.is_empty then
  340. --| *** TODO error: the loadpath line does not contain a valid path
  341. not_yet_implemented
  342. end
  343. sublocation.copy(dir.last_entry)
  344. end
  345. if ft.is_directory(sublocation) then
  346. a_locations.add_last(sublocation.intern)
  347. elseif ft.is_file(sublocation) then
  348. log.trace.put_string(name)
  349. log.trace.put_string(once ": adding child cluster from loadpath ")
  350. log.trace.put_line(sublocation)
  351. a_children.add_last(create {LIBERTY_CLUSTER}.make_from_loadpath(sublocation, Current, root));
  352. else
  353. std_error.put_line(once "*** Warning: ignored location: " + sublocation)
  354. end
  355. end
  356. end
  357. dir: BASIC_DIRECTORY
  358. tfr: TEXT_FILE_READ is
  359. once
  360. create Result.make
  361. end
  362. ft: FILE_TOOLS
  363. children: TRAVERSABLE[LIBERTY_CLUSTER]
  364. class_names: HASHED_DICTIONARY[FIXED_STRING, FIXED_STRING]
  365. root: LIBERTY_CLUSTER
  366. parent: LIBERTY_CLUSTER
  367. loadpath_entry: POSIX_PATH_NAME is
  368. once
  369. create Result.make_empty
  370. end
  371. loadpath_notation: UNIX_DIRECTORY_NOTATION is
  372. once
  373. create Result
  374. end
  375. env: LIBERTY_ENVIRONMENT
  376. find_mark: INTEGER
  377. logged: BOOLEAN
  378. find_mark_counter: COUNTER is
  379. once
  380. create Result
  381. end
  382. invariant
  383. not name.is_empty
  384. class_names /= Void
  385. locations.for_all(agent ft.is_directory)
  386. root.depth = 0
  387. depth = 0 implies root = Current
  388. parent /= Void
  389. Current = root implies Current = parent
  390. depth >= 0
  391. end