plugin.scm 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2019, 2020 Julien Lepiller <julien@lepiller.eu>
  3. ;;;
  4. ;;; This file is part of GNU Guix.
  5. ;;;
  6. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; GNU Guix is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (guix build maven plugin)
  19. #:use-module (guix build maven java)
  20. #:use-module (ice-9 textual-ports)
  21. #:use-module (ice-9 match)
  22. #:use-module (srfi srfi-9)
  23. #:export (generate-mojo-from-files
  24. default-convert-type
  25. maven-convert-type))
  26. (define-record-type mojo
  27. (make-mojo package name goal description requires-dependency-collection
  28. requires-dependency-resolution requires-direct-invocation?
  29. requires-project? requires-reports? aggregator? requires-online?
  30. inherited-by-default? instantiation-strategy execution-strategy
  31. since thread-safe? phase parameters components)
  32. mojo?
  33. (package mojo-package)
  34. (name mojo-name)
  35. (goal mojo-goal)
  36. (description mojo-description)
  37. (requires-dependency-collection mojo-requires-dependency-collection)
  38. (requires-dependency-resolution mojo-requires-dependency-resolution)
  39. (requires-direct-invocation? mojo-requires-direct-invocation?)
  40. (requires-project? mojo-requires-project?)
  41. (requires-reports? mojo-requires-reports?)
  42. (aggregator? mojo-aggregator?)
  43. (requires-online? mojo-requires-online?)
  44. (inherited-by-default? mojo-inherited-by-default?)
  45. (instantiation-strategy mojo-instantiation-strategy)
  46. (execution-strategy mojo-execution-strategy)
  47. (since mojo-since)
  48. (thread-safe? mojo-thread-safe?)
  49. (phase mojo-phase)
  50. (parameters mojo-parameters)
  51. (components mojo-components))
  52. (define* (update-mojo mojo
  53. #:key
  54. (package (mojo-package mojo))
  55. (name (mojo-name mojo))
  56. (goal (mojo-goal mojo))
  57. (description (mojo-description mojo))
  58. (requires-dependency-collection (mojo-requires-dependency-collection mojo))
  59. (requires-dependency-resolution (mojo-requires-dependency-resolution mojo))
  60. (requires-direct-invocation? (mojo-requires-direct-invocation? mojo))
  61. (requires-project? (mojo-requires-project? mojo))
  62. (requires-reports? (mojo-requires-reports? mojo))
  63. (aggregator? (mojo-aggregator? mojo))
  64. (requires-online? (mojo-requires-online? mojo))
  65. (inherited-by-default? (mojo-inherited-by-default? mojo))
  66. (instantiation-strategy (mojo-instantiation-strategy mojo))
  67. (execution-strategy (mojo-execution-strategy mojo))
  68. (since (mojo-since mojo))
  69. (thread-safe? (mojo-thread-safe? mojo))
  70. (phase (mojo-phase mojo))
  71. (parameters (mojo-parameters mojo))
  72. (components (mojo-components mojo)))
  73. (make-mojo package name goal description requires-dependency-collection
  74. requires-dependency-resolution requires-direct-invocation?
  75. requires-project? requires-reports? aggregator? requires-online?
  76. inherited-by-default? instantiation-strategy execution-strategy
  77. since thread-safe? phase parameters components))
  78. (define-record-type mojo-parameter
  79. (make-mojo-parameter name type since required editable property description
  80. configuration)
  81. mojo-parameter?
  82. (name mojo-parameter-name)
  83. (type mojo-parameter-type)
  84. (since mojo-parameter-since)
  85. (required mojo-parameter-required)
  86. (editable mojo-parameter-editable)
  87. (property mojo-parameter-property)
  88. (description mojo-parameter-description)
  89. (configuration mojo-parameter-configuration))
  90. (define* (update-mojo-parameter mojo-parameter
  91. #:key (name (mojo-parameter-name mojo-parameter))
  92. (type (mojo-parameter-type mojo-parameter))
  93. (since (mojo-parameter-since mojo-parameter))
  94. (required (mojo-parameter-required mojo-parameter))
  95. (editable (mojo-parameter-editable mojo-parameter))
  96. (property (mojo-parameter-property mojo-parameter))
  97. (description (mojo-parameter-description mojo-parameter))
  98. (configuration (mojo-parameter-configuration mojo-parameter)))
  99. (make-mojo-parameter name type since required editable property description
  100. configuration))
  101. (define-record-type <mojo-component>
  102. (make-mojo-component field role hint)
  103. mojo-component?
  104. (field mojo-component-field)
  105. (role mojo-component-role)
  106. (hint mojo-component-hint))
  107. (define* (update-mojo-component mojo-component
  108. #:key (field (mojo-component-field mojo-component))
  109. (role (mojo-component-role mojo-component))
  110. (hint (mojo-component-hint mojo-component)))
  111. (make-mojo-component field role hint))
  112. (define (generate-mojo-parameter mojo-parameter)
  113. `(parameter (name ,(mojo-parameter-name mojo-parameter))
  114. (type ,(mojo-parameter-type mojo-parameter))
  115. ,@(if (mojo-parameter-since mojo-parameter)
  116. `(since (mojo-parameter-since mojo-parameter))
  117. '())
  118. (required ,(if (mojo-parameter-required mojo-parameter) "true" "false"))
  119. (editable ,(if (mojo-parameter-editable mojo-parameter) "true" "false"))
  120. (description ,(mojo-parameter-description mojo-parameter))))
  121. (define (generate-mojo-configuration mojo-parameter)
  122. (let ((config (mojo-parameter-configuration mojo-parameter)))
  123. (if (or config (mojo-parameter-property mojo-parameter))
  124. `(,(string->symbol (mojo-parameter-name mojo-parameter))
  125. (@ ,@(cons (list 'implementation (mojo-parameter-type mojo-parameter))
  126. (or config '())))
  127. ,@(if (mojo-parameter-property mojo-parameter)
  128. (list (string-append "${" (mojo-parameter-property mojo-parameter)
  129. "}"))
  130. '()))
  131. #f)))
  132. (define (generate-mojo-component mojo-component)
  133. (let ((role (mojo-component-role mojo-component))
  134. (field (mojo-component-field mojo-component))
  135. (hint (mojo-component-hint mojo-component)))
  136. `(requirement
  137. (role ,role)
  138. ,@(if hint
  139. `((role-hint ,hint))
  140. '())
  141. (field-name ,field))))
  142. (define (generate-mojo mojo)
  143. `(mojo
  144. (goal ,(mojo-goal mojo))
  145. (description ,(mojo-description mojo))
  146. ,@(let ((val (mojo-requires-dependency-collection mojo)))
  147. (if val
  148. `((requiresDependencyCollection ,val))
  149. '()))
  150. ,@(let ((val (mojo-requires-dependency-resolution mojo)))
  151. (if val
  152. `((requiresDependencyResolution ,val))
  153. '()))
  154. ,@(let ((val (mojo-requires-direct-invocation? mojo)))
  155. (if val
  156. `((requiresDirectInvocation ,val))
  157. '()))
  158. ,@(let ((val (mojo-requires-project? mojo)))
  159. (if val
  160. `((requiresProject ,val))
  161. '()))
  162. ,@(let ((val (mojo-requires-reports? mojo)))
  163. (if val
  164. `((requiresReports ,val))
  165. '()))
  166. ,@(let ((val (mojo-aggregator? mojo)))
  167. (if val
  168. `((aggregator ,val))
  169. '()))
  170. ,@(let ((val (mojo-requires-online? mojo)))
  171. (if val
  172. `((requiresOnline ,val))
  173. '()))
  174. ,@(let ((val (mojo-inherited-by-default? mojo)))
  175. (if val
  176. `((inheritedByDefault ,val))
  177. '()))
  178. ,@(let ((phase (mojo-phase mojo)))
  179. (if phase
  180. `((phase ,phase))
  181. '()))
  182. (implementation ,(string-append (mojo-package mojo) "." (mojo-name mojo)))
  183. (language "java")
  184. (instantiationStrategy ,(mojo-instantiation-strategy mojo))
  185. (executionStrategy ,(mojo-execution-strategy mojo))
  186. ,@(let ((since (mojo-since mojo)))
  187. (if since
  188. `((since ,since))
  189. '()))
  190. ,@(let ((val (mojo-thread-safe? mojo)))
  191. (if val
  192. `((threadSafe ,val))
  193. '()))
  194. (parameters
  195. ,(map generate-mojo-parameter (mojo-parameters mojo)))
  196. (configuration
  197. ,@(filter (lambda (a) a) (map generate-mojo-configuration (mojo-parameters mojo))))
  198. (requirements
  199. ,@(map generate-mojo-component (mojo-components mojo)))))
  200. (define (default-convert-type type)
  201. (cond
  202. ((equal? type "String") "java.lang.String")
  203. ((equal? type "String[]") "java.lang.String[]")
  204. ((equal? type "File") "java.io.File")
  205. ((equal? type "File[]") "java.io.File[]")
  206. ((equal? type "List") "java.util.List")
  207. ((equal? type "Boolean") "java.lang.Boolean")
  208. ((equal? type "Properties") "java.util.Properties")
  209. ((and (> (string-length type) 5)
  210. (equal? (substring type 0 4) "Map<"))
  211. "java.util.Map")
  212. ((and (> (string-length type) 6)
  213. (equal? (substring type 0 5) "List<"))
  214. "java.util.List")
  215. ((and (> (string-length type) 15)
  216. (equal? (substring type 0 14) "LinkedHashSet<"))
  217. "java.util.LinkedHashSet")
  218. (else type)))
  219. (define (maven-convert-type type)
  220. (cond
  221. ((equal? type "MavenProject")
  222. "org.apache.maven.project.MavenProject")
  223. (else (default-convert-type type))))
  224. (define (update-mojo-from-file mojo file convert-type)
  225. (define parse-tree (parse-java-file file))
  226. (define (update-mojo-from-attrs mojo attrs)
  227. (let loop ((mojo mojo) (attrs attrs))
  228. (match attrs
  229. ('() mojo)
  230. ((attr attrs ...)
  231. (match attr
  232. (('annotation-attr ('attr-name name) ('attr-value value))
  233. (cond
  234. ((equal? name "name")
  235. (loop (update-mojo mojo #:goal value) attrs))
  236. ((equal? name "defaultPhase")
  237. (let* ((phase (car (reverse (string-split value #\.))))
  238. (phase (string-downcase phase))
  239. (phase (string-join (string-split phase #\_) "-")))
  240. (loop (update-mojo mojo #:phase phase) attrs)))
  241. ((equal? name "requiresProject")
  242. (loop (update-mojo mojo #:requires-project? value) attrs))
  243. ((equal? name "threadSafe")
  244. (loop (update-mojo mojo #:thread-safe? value) attrs))
  245. ((equal? name "aggregator")
  246. (loop (update-mojo mojo #:aggregator? value) attrs))
  247. ((equal? name "requiresDependencyCollection")
  248. (loop
  249. (update-mojo mojo #:requires-dependency-collection
  250. (match value
  251. ("ResolutionScope.COMPILE" "compile")
  252. ("ResolutionScope.COMPILE_PLUS_RUNTIME"
  253. "compile+runtime")
  254. ("ResolutionScope.RUNTIME" "runtime")
  255. ("ResolutionScope.RUNTIME_PLUS_SYSTEM"
  256. "runtime+system")
  257. ("ResolutionScope.TEST" "test")
  258. ("ResolutionScope.PROVIDED" "provided")
  259. ("ResolutionScope.SYSTEM" "system")
  260. ("ResolutionScope.IMPORT" "import")))
  261. attrs))
  262. ((equal? name "requiresDependencyResolution")
  263. (loop
  264. (update-mojo mojo #:requires-dependency-resolution
  265. (match value
  266. ("ResolutionScope.COMPILE" "compile")
  267. ("ResolutionScope.COMPILE_PLUS_RUNTIME"
  268. "compile+runtime")
  269. ("ResolutionScope.RUNTIME" "runtime")
  270. ("ResolutionScope.RUNTIME_PLUS_SYSTEM"
  271. "runtime+system")
  272. ("ResolutionScope.TEST" "test")
  273. ("ResolutionScope.PROVIDED" "provided")
  274. ("ResolutionScope.SYSTEM" "system")
  275. ("ResolutionScope.IMPORT" "import")))
  276. attrs))
  277. (else
  278. (throw 'not-found-attr name))))
  279. ((attrs ...) (loop mojo attrs))
  280. (_ (loop mojo attrs)))))))
  281. (define (string->attr name)
  282. (define (string-split-upper s)
  283. (let ((i (string-index s char-set:upper-case)))
  284. (if (and i (> i 0))
  285. (cons (substring s 0 i) (string-split-upper (substring s i)))
  286. (list s))))
  287. (string->symbol
  288. (string-join (map string-downcase (string-split-upper name)) "-")))
  289. (define (update-mojo-parameter-from-attrs mojo-parameter attrs)
  290. (match attrs
  291. ('() mojo-parameter)
  292. (('annotation-attr ('attr-name name) 'attr-value)
  293. mojo-parameter)
  294. ;(update-mojo-parameter-from-attrs mojo-parameter
  295. ; `(annotation-attr (attr-name ,name) (attr-value ""))))
  296. (('annotation-attr ('attr-name name) ('attr-value value))
  297. (cond
  298. ((equal? name "editable")
  299. (update-mojo-parameter mojo-parameter #:editable value))
  300. ((equal? name "required")
  301. (update-mojo-parameter mojo-parameter #:required value))
  302. ((equal? name "property")
  303. (update-mojo-parameter mojo-parameter #:property value))
  304. (else
  305. (update-mojo-parameter mojo-parameter
  306. #:configuration
  307. (cons
  308. (list (string->attr name) value)
  309. (or
  310. (mojo-parameter-configuration mojo-parameter)
  311. '()))))))
  312. ((attr attrs ...)
  313. (update-mojo-parameter-from-attrs
  314. (update-mojo-parameter-from-attrs mojo-parameter attr)
  315. attrs))))
  316. (define (update-mojo-component-from-attrs mojo-component inverse-import attrs)
  317. (match attrs
  318. ('() mojo-component)
  319. ((attr attrs ...)
  320. (match attr
  321. (('annotation-attr ('attr-name name) ('attr-value value))
  322. (cond
  323. ((equal? name "role")
  324. (update-mojo-component-from-attrs
  325. (update-mojo-component mojo-component
  326. #:role (select-import inverse-import value convert-type))
  327. inverse-import
  328. attrs))
  329. ((equal? name "hint")
  330. (update-mojo-component-from-attrs
  331. (update-mojo-component mojo-component #:hint value)
  332. inverse-import
  333. attrs))
  334. (else (throw 'not-found-attr name))))
  335. ((attrss ...)
  336. (update-mojo-component-from-attrs
  337. mojo-component inverse-import (append attrss attrs)))))))
  338. (define (add-mojo-parameter parameters name type last-comment attrs inverse-import)
  339. (let loop ((parameters parameters))
  340. (match parameters
  341. ('() (list (update-mojo-parameter-from-attrs
  342. (make-mojo-parameter
  343. ;; name convert since required editable property comment config
  344. name (select-import inverse-import type convert-type)
  345. #f #f #t #f last-comment #f)
  346. attrs)))
  347. ((parameter parameters ...)
  348. (if (equal? (mojo-parameter-name parameter) name)
  349. (cons (update-mojo-parameter-from-attrs
  350. (make-mojo-parameter
  351. name (select-import inverse-import type convert-type)
  352. #f #f #t #f last-comment #f)
  353. attrs) parameters)
  354. (cons parameter (loop parameters)))))))
  355. (define (update-mojo-from-class-content mojo inverse-import content)
  356. (let loop ((content content)
  357. (mojo mojo)
  358. (last-comment #f))
  359. (match content
  360. ('() mojo)
  361. ((('comment ('annotation-pat _ ...) last-comment) content ...)
  362. (loop content mojo last-comment))
  363. ((('comment last-comment) content ...)
  364. (loop content mojo last-comment))
  365. ((('param-pat ('annotation-pat annot-name attrs ...) ('type-name type)
  366. ('param-name name)) content ...)
  367. (cond
  368. ((equal? annot-name "Parameter")
  369. (loop content
  370. (update-mojo mojo
  371. #:parameters
  372. (add-mojo-parameter
  373. (mojo-parameters mojo) name type last-comment
  374. attrs inverse-import))
  375. #f))
  376. ((equal? annot-name "Component")
  377. (loop content
  378. (update-mojo mojo
  379. #:components
  380. (cons (update-mojo-component-from-attrs
  381. (make-mojo-component
  382. name
  383. (select-import inverse-import type
  384. convert-type)
  385. #f)
  386. inverse-import
  387. attrs)
  388. (mojo-components mojo)))
  389. #f))
  390. (else (throw 'not-found-annot annot-name))))
  391. ((('class-pat _ ...) content ...)
  392. (loop content mojo #f))
  393. ((('param-pat _ ...) content ...)
  394. (loop content mojo #f))
  395. ((('method-pat _ ...) content ...)
  396. (loop content mojo #f)))))
  397. (define (update-inverse-import inverse-import package)
  398. (let ((package-name (car (reverse (string-split package #\.)))))
  399. (cons (cons package-name package) inverse-import)))
  400. (define (select-import inverse-import package convert-type)
  401. (let* ((package (car (string-split package #\<)))
  402. (package (string-split package #\.))
  403. (rest (reverse (cdr package)))
  404. (rest (cond
  405. ((null? rest) '())
  406. ((equal? (car rest) "class") (cdr rest))
  407. (else rest)))
  408. (base (or (assoc-ref inverse-import (car package)) (car package))))
  409. (convert-type (string-join (cons base rest) "."))))
  410. (let loop ((content parse-tree)
  411. (mojo mojo)
  412. (inverse-import '())
  413. (last-comment #f))
  414. (if (null? content)
  415. mojo
  416. (match content
  417. ((tls content ...)
  418. (match tls
  419. (('package package)
  420. (loop content (update-mojo mojo #:package package) inverse-import
  421. last-comment))
  422. (('import-pat package)
  423. (loop content mojo (update-inverse-import inverse-import package)
  424. last-comment))
  425. (('comment last-comment)
  426. (loop content mojo inverse-import last-comment))
  427. (('class-pat class-tls ...)
  428. (let loop2 ((class-tls class-tls) (mojo mojo))
  429. (match class-tls
  430. ('() (loop content mojo inverse-import #f))
  431. (((? string? name) class-tls ...)
  432. (loop2 class-tls (update-mojo mojo #:name name)))
  433. ((('annotation-pat annot-name (attrs ...)) class-tls ...)
  434. (loop2
  435. class-tls
  436. (update-mojo-from-attrs mojo attrs)))
  437. ((('class-body class-content ...) class-tls ...)
  438. (loop2
  439. class-tls
  440. (update-mojo-from-class-content
  441. mojo inverse-import class-content)))
  442. ((_ class-tls ...)
  443. (loop2 class-tls mojo)))))
  444. (_
  445. (loop content mojo inverse-import last-comment))))))))
  446. (define (generate-mojo-from-files convert-type . files)
  447. (let ((mojo (make-mojo #f #f #f #f #f #f #f #f #f #f #f #f "per-lookup"
  448. "once-per-session" #f #f #f '() '())))
  449. (let loop ((files files) (mojo mojo))
  450. (if (null? files)
  451. (generate-mojo mojo)
  452. (loop
  453. (cdr files)
  454. (update-mojo-from-file
  455. (update-mojo mojo
  456. #:package #f
  457. #:name #f
  458. #:goal #f
  459. #:description #f
  460. #:requires-dependency-resolution #f
  461. #:requires-direct-invocation? #f
  462. #:requires-project? #f
  463. #:requires-reports? #f
  464. #:aggregator? #f
  465. #:requires-online? #f
  466. #:inherited-by-default? #f
  467. #:instantiation-strategy "per-lookup"
  468. #:execution-strategy "once-per-session"
  469. #:since #f
  470. #:thread-safe? #f
  471. #:phase #f)
  472. (car files)
  473. convert-type))))))