gnu: Add emacs-slime.
[jackhill/guix/guix.git] / gnu / packages / ocaml.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2013 Cyril Roelandt <tipecaml@gmail.com>
3 ;;; Copyright © 2014, 2015 Mark H Weaver <mhw@netris.org>
4 ;;; Copyright © 2015 Andreas Enge <andreas@enge.fr>
5 ;;; Copyright © 2015 David Hashe <david.hashe@dhashe.com>
6 ;;; Copyright © 2016 Eric Bavier <bavier@member.fsf.org>
7 ;;;
8 ;;; This file is part of GNU Guix.
9 ;;;
10 ;;; GNU Guix is free software; you can redistribute it and/or modify it
11 ;;; under the terms of the GNU General Public License as published by
12 ;;; the Free Software Foundation; either version 3 of the License, or (at
13 ;;; your option) any later version.
14 ;;;
15 ;;; GNU Guix is distributed in the hope that it will be useful, but
16 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;;; GNU General Public License for more details.
19 ;;;
20 ;;; You should have received a copy of the GNU General Public License
21 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
22
23 (define-module (gnu packages ocaml)
24 #:use-module ((guix licenses) #:hide (zlib))
25 #:use-module (guix packages)
26 #:use-module (guix download)
27 #:use-module (guix svn-download)
28 #:use-module (guix utils)
29 #:use-module (guix build-system gnu)
30 #:use-module (gnu packages)
31 #:use-module (gnu packages gcc)
32 #:use-module (gnu packages gnome)
33 #:use-module (gnu packages gtk)
34 #:use-module (gnu packages base)
35 #:use-module (gnu packages emacs)
36 #:use-module (gnu packages texinfo)
37 #:use-module (gnu packages pkg-config)
38 #:use-module (gnu packages compression)
39 #:use-module (gnu packages xorg)
40 #:use-module (gnu packages texlive)
41 #:use-module (gnu packages ghostscript)
42 #:use-module (gnu packages lynx)
43 #:use-module (gnu packages perl)
44 #:use-module (gnu packages python)
45 #:use-module (gnu packages ncurses)
46 #:use-module (gnu packages version-control)
47 #:use-module (gnu packages curl))
48
49 (define-public ocaml
50 (package
51 (name "ocaml")
52 (version "4.02.3")
53 (source (origin
54 (method url-fetch)
55 (uri (string-append
56 "http://caml.inria.fr/pub/distrib/ocaml-"
57 (version-major+minor version)
58 "/ocaml-" version ".tar.xz"))
59 (sha256
60 (base32
61 "1qwwvy8nzd87hk8rd9sm667nppakiapnx4ypdwcrlnav2dz6kil3"))))
62 (build-system gnu-build-system)
63 (native-inputs
64 `(("perl" ,perl)
65 ("pkg-config" ,pkg-config)))
66 (inputs
67 `(("libx11" ,libx11)
68 ;; For libiberty, needed for objdump support.
69 ("gcc:lib" ,(canonical-package gcc-4.9) "lib")
70 ("zlib" ,zlib))) ;also needed for objdump support
71 (arguments
72 `(#:modules ((guix build gnu-build-system)
73 (guix build utils)
74 (web server))
75 #:phases
76 (modify-phases %standard-phases
77 (add-after 'unpack 'patch-/bin/sh-references
78 (lambda* (#:key inputs #:allow-other-keys)
79 (let* ((sh (string-append (assoc-ref inputs "bash")
80 "/bin/sh"))
81 (quoted-sh (string-append "\"" sh "\"")))
82 (with-fluids ((%default-port-encoding #f))
83 (for-each (lambda (file)
84 (substitute* file
85 (("\"/bin/sh\"")
86 (begin
87 (format (current-error-port) "\
88 patch-/bin/sh-references: ~a: changing `\"/bin/sh\"' to `~a'~%"
89 file quoted-sh)
90 quoted-sh))))
91 (find-files "." "\\.ml$"))
92 #t))))
93 (replace 'configure
94 (lambda* (#:key outputs #:allow-other-keys)
95 (let* ((out (assoc-ref outputs "out"))
96 (mandir (string-append out "/share/man")))
97 ;; Custom configure script doesn't recognize
98 ;; --prefix=<PREFIX> syntax (with equals sign).
99 (zero? (system* "./configure"
100 "--prefix" out
101 "--mandir" mandir)))))
102 (replace 'build
103 (lambda _
104 (zero? (system* "make" "-j" (number->string
105 (parallel-job-count))
106 "world.opt"))))
107 (delete 'check)
108 (add-after 'install 'check
109 (lambda _
110 (with-directory-excursion "testsuite"
111 (zero? (system* "make" "all")))))
112 (add-before 'check 'prepare-socket-test
113 (lambda _
114 (format (current-error-port)
115 "Spawning local test web server on port 8080~%")
116 (when (zero? (primitive-fork))
117 (run-server (lambda (request request-body)
118 (values '((content-type . (text/plain)))
119 "Hello!"))
120 'http '(#:port 8080)))
121 (let ((file "testsuite/tests/lib-threads/testsocket.ml"))
122 (format (current-error-port)
123 "Patching ~a to use localhost port 8080~%"
124 file)
125 (substitute* file
126 (("caml.inria.fr") "localhost")
127 (("80") "8080")
128 (("HTTP1.0") "HTTP/1.0"))
129 #t))))))
130 (home-page "https://ocaml.org/")
131 (synopsis "The OCaml programming language")
132 (description
133 "OCaml is a general purpose industrial-strength programming language with
134 an emphasis on expressiveness and safety. Developed for more than 20 years at
135 Inria it benefits from one of the most advanced type systems and supports
136 functional, imperative and object-oriented styles of programming.")
137 ;; The compiler is distributed under qpl1.0 with a change to choice of
138 ;; law: the license is governed by the laws of France. The library is
139 ;; distributed under lgpl2.0.
140 (license (list qpl lgpl2.0))))
141
142 (define-public opam
143 (package
144 (name "opam")
145 (version "1.1.1")
146 (source (origin
147 (method url-fetch)
148 ;; Use the '-full' version, which includes all the dependencies.
149 (uri (string-append
150 "https://github.com/ocaml/opam/releases/download/"
151 version "/opam-full-" version ".tar.gz")
152 ;; (string-append "https://github.com/ocaml/opam/archive/"
153 ;; version ".tar.gz")
154 )
155 (sha256
156 (base32
157 "1frzqkx6yn1pnyd9qz3bv3rbwv74bmc1xji8kl41r1dkqzfl3xqv"))))
158 (build-system gnu-build-system)
159 (arguments
160 '(;; Sometimes, 'make -jX' would fail right after ./configure with
161 ;; "Fatal error: exception End_of_file".
162 #:parallel-build? #f
163
164 ;; For some reason, 'ocp-build' needs $TERM to be set.
165 #:make-flags '("TERM=screen")
166 #:test-target "tests"
167
168 ;; FIXME: There's an obscure test failure:
169 ;; …/_obuild/opam/opam.asm install P1' failed.
170 #:tests? #f
171
172 #:phases (alist-cons-before
173 'build 'pre-build
174 (lambda* (#:key inputs #:allow-other-keys)
175 (let ((bash (assoc-ref inputs "bash")))
176 (substitute* "src/core/opamSystem.ml"
177 (("\"/bin/sh\"")
178 (string-append "\"" bash "/bin/sh\"")))))
179 (alist-cons-before
180 'check 'pre-check
181 (lambda _
182 (setenv "HOME" (getcwd))
183 (and (system "git config --global user.email guix@gnu.org")
184 (system "git config --global user.name Guix")))
185 %standard-phases))))
186 (native-inputs
187 `(("git" ,git) ;for the tests
188 ("python" ,python))) ;for the tests
189 (inputs
190 `(("ocaml" ,ocaml)
191 ("ncurses" ,ncurses)
192 ("curl" ,curl)))
193 (home-page "http://opam.ocamlpro.com/")
194 (synopsis "Package manager for OCaml")
195 (description
196 "OPAM is a tool to manage OCaml packages. It supports multiple
197 simultaneous compiler installations, flexible package constraints, and a
198 Git-friendly development workflow.")
199
200 ;; The 'LICENSE' file waives some requirements compared to LGPLv3.
201 (license lgpl3)))
202
203 (define-public camlp4
204 (package
205 (name "camlp4")
206 (version "4.02+6")
207 (source (origin
208 (method url-fetch)
209 (uri (string-append "https://github.com/ocaml/camlp4/archive/"
210 version ".tar.gz"))
211 (sha256
212 (base32
213 "0icdfzhsbgf89925gc8gl3fm8z2xzszzlib0v9dj5wyzkyv3a342"))
214 (file-name (string-append name "-" version ".tar.gz"))))
215 (build-system gnu-build-system)
216 (native-inputs `(("ocaml" ,ocaml)
217 ("which" ,which)))
218 (inputs `(("ocaml" ,ocaml)))
219 (arguments
220 '(#:tests? #f ;no documented test target
221 #:phases (modify-phases %standard-phases
222 (replace
223 'configure
224 (lambda* (#:key outputs #:allow-other-keys)
225 ;; This is a home-made 'configure' script.
226 (let ((out (assoc-ref outputs "out")))
227 (zero? (system* "./configure"
228 (string-append "--libdir=" out "/lib")
229 (string-append "--bindir=" out "/bin")
230 (string-append "--pkgdir=" out)))))))))
231 (home-page "https://github.com/ocaml/camlp4")
232 (synopsis "Write parsers in OCaml")
233 (description
234 "Camlp4 is a software system for writing extensible parsers for
235 programming languages. It provides a set of OCaml libraries that are used to
236 define grammars as well as loadable syntax extensions of such grammars.
237 Camlp4 stands for Caml Preprocessor and Pretty-Printer and one of its most
238 important applications is the definition of domain-specific extensions of the
239 syntax of OCaml.")
240
241 ;; This is LGPLv2 with an exception that allows packages statically-linked
242 ;; against the library to be released under any terms.
243 (license lgpl2.0)))
244
245 (define-public camlp5
246 (package
247 (name "camlp5")
248 (version "6.14")
249 (source (origin
250 (method url-fetch)
251 (uri (string-append "http://camlp5.gforge.inria.fr/distrib/src/"
252 name "-" version ".tgz"))
253 (sha256
254 (base32
255 "1ql04iyvclpyy9805kpddc4ndjb5d0qg4shhi2fc6bixi49fvy89"))))
256 (build-system gnu-build-system)
257 (inputs
258 `(("ocaml" ,ocaml)))
259 (arguments
260 `(#:tests? #f ; XXX TODO figure out how to run the tests
261 #:phases
262 (modify-phases %standard-phases
263 (replace 'configure
264 (lambda* (#:key outputs #:allow-other-keys)
265 (let* ((out (assoc-ref outputs "out"))
266 (mandir (string-append out "/share/man")))
267 ;; Custom configure script doesn't recognize
268 ;; --prefix=<PREFIX> syntax (with equals sign).
269 (zero? (system* "./configure"
270 "--prefix" out
271 "--mandir" mandir)))))
272 (replace 'build
273 (lambda _
274 (zero? (system* "make" "-j" (number->string
275 (parallel-job-count))
276 "world.opt")))))))
277 (home-page "http://camlp5.gforge.inria.fr/")
278 (synopsis "Pre-processor Pretty Printer for OCaml")
279 (description
280 "Camlp5 is a Pre-Processor-Pretty-Printer for Objective Caml. It offers
281 tools for syntax (Stream Parsers and Grammars) and the ability to modify the
282 concrete syntax of the language (Quotations, Syntax Extensions).")
283 ;; Most files are distributed under bsd-3, but ocaml_stuff/* is under qpl.
284 (license (list bsd-3 qpl))))
285
286 (define-public hevea
287 (package
288 (name "hevea")
289 (version "2.28")
290 (source (origin
291 (method url-fetch)
292 (uri (string-append "http://hevea.inria.fr/old/"
293 name "-" version ".tar.gz"))
294 (sha256
295 (base32
296 "14fns13wlnpiv9i05841kvi3cq4b9v2sw5x3ff6ziws28q701qnd"))))
297 (build-system gnu-build-system)
298 (inputs
299 `(("ocaml" ,ocaml)))
300 (arguments
301 `(#:tests? #f ; no test suite
302 #:make-flags (list (string-append "PREFIX=" %output))
303 #:phases (modify-phases %standard-phases
304 (delete 'configure)
305 (add-before 'build 'patch-/bin/sh
306 (lambda _
307 (substitute* "_tags"
308 (("/bin/sh") (which "sh")))
309 #t)))))
310 (home-page "http://hevea.inria.fr/")
311 (synopsis "LaTeX to HTML translator")
312 (description
313 "HeVeA is a LaTeX to HTML translator that generates modern HTML 5. It is
314 written in Objective Caml.")
315 (license qpl)))
316
317 (define-public coq
318 (package
319 (name "coq")
320 (version "8.4pl6")
321 (source (origin
322 (method url-fetch)
323 (uri (string-append "https://coq.inria.fr/distrib/V" version
324 "/files/" name "-" version ".tar.gz"))
325 (sha256
326 (base32
327 "1mpbj4yf36kpjg2v2sln12i8dzqn8rag6fd07hslj2lpm4qs4h55"))))
328 (build-system gnu-build-system)
329 (native-inputs
330 `(("texlive" ,texlive)
331 ("hevea" ,hevea)))
332 (inputs
333 `(("ocaml" ,ocaml)
334 ("camlp5" ,camlp5)))
335 (arguments
336 `(#:phases
337 (modify-phases %standard-phases
338 (replace 'configure
339 (lambda* (#:key outputs #:allow-other-keys)
340 (let* ((out (assoc-ref outputs "out"))
341 (mandir (string-append out "/share/man"))
342 (browser "icecat -remote \"OpenURL(%s,new-tab)\""))
343 (zero? (system* "./configure"
344 "--prefix" out
345 "--mandir" mandir
346 "--browser" browser)))))
347 (replace 'build
348 (lambda _
349 (zero? (system* "make" "-j" (number->string
350 (parallel-job-count))
351 "world"))))
352 (delete 'check)
353 (add-after 'install 'check
354 (lambda _
355 (with-directory-excursion "test-suite"
356 (zero? (system* "make"))))))))
357 (home-page "https://coq.inria.fr")
358 (synopsis "Proof assistant for higher-order logic")
359 (description
360 "Coq is a proof assistant for higher-order logic, which allows the
361 development of computer programs consistent with their formal specification.
362 It is developed using Objective Caml and Camlp5.")
363 ;; The code is distributed under lgpl2.1.
364 ;; Some of the documentation is distributed under opl1.0+.
365 (license (list lgpl2.1 opl1.0+))))
366
367 (define-public proof-general
368 (package
369 (name "proof-general")
370 (version "4.2")
371 (source (origin
372 (method url-fetch)
373 (uri (string-append
374 "http://proofgeneral.inf.ed.ac.uk/releases/"
375 "ProofGeneral-" version ".tgz"))
376 (sha256
377 (base32
378 "09qb0myq66fw17v4ziz401ilsb5xlxz1nl2wsp69d0vrfy0bcrrm"))))
379 (build-system gnu-build-system)
380 (native-inputs
381 `(("which" ,which)
382 ("emacs" ,emacs-no-x)
383 ("texinfo" ,texinfo)))
384 (inputs
385 `(("host-emacs" ,emacs)
386 ("perl" ,perl)
387 ("coq" ,coq)))
388 (arguments
389 `(#:tests? #f ; no check target
390 #:make-flags (list (string-append "PREFIX=" %output)
391 (string-append "DEST_PREFIX=" %output))
392 #:modules ((guix build gnu-build-system)
393 (guix build utils)
394 (guix build emacs-utils))
395 #:imported-modules (,@%gnu-build-system-modules
396 (guix build emacs-utils))
397 #:phases
398 (modify-phases %standard-phases
399 (delete 'configure)
400 (add-after 'unpack 'disable-byte-compile-error-on-warn
401 (lambda _
402 (substitute* "Makefile"
403 (("\\(setq byte-compile-error-on-warn t\\)")
404 "(setq byte-compile-error-on-warn nil)"))
405 #t))
406 (add-after 'unpack 'patch-hardcoded-paths
407 (lambda* (#:key inputs outputs #:allow-other-keys)
408 (let ((out (assoc-ref outputs "out"))
409 (coq (assoc-ref inputs "coq"))
410 (emacs (assoc-ref inputs "host-emacs")))
411 (define (coq-prog name)
412 (string-append coq "/bin/" name))
413 (emacs-substitute-variables "coq/coq.el"
414 ("coq-prog-name" (coq-prog "coqtop"))
415 ("coq-compiler" (coq-prog "coqc"))
416 ("coq-dependency-analyzer" (coq-prog "coqdep")))
417 (substitute* "Makefile"
418 (("/sbin/install-info") "install-info"))
419 (substitute* "bin/proofgeneral"
420 (("^PGHOMEDEFAULT=.*" all)
421 (string-append all
422 "PGHOME=$PGHOMEDEFAULT\n"
423 "EMACS=" emacs "/bin/emacs")))
424 #t)))
425 (add-after 'unpack 'clean
426 (lambda _
427 ;; Delete the pre-compiled elc files for Emacs 23.
428 (zero? (system* "make" "clean"))))
429 (add-after 'install 'install-doc
430 (lambda* (#:key make-flags #:allow-other-keys)
431 ;; XXX FIXME avoid building/installing pdf files,
432 ;; due to unresolved errors building them.
433 (substitute* "Makefile"
434 ((" [^ ]*\\.pdf") ""))
435 (zero? (apply system* "make" "install-doc"
436 make-flags)))))))
437 (home-page "http://proofgeneral.inf.ed.ac.uk/")
438 (synopsis "Generic front-end for proof assistants based on Emacs")
439 (description
440 "Proof General is a major mode to turn Emacs into an interactive proof
441 assistant to write formal mathematical proofs using a variety of theorem
442 provers.")
443 (license gpl2+)))
444
445 (define-public lablgtk
446 (package
447 (name "lablgtk")
448 (version "2.18.3")
449 (source
450 (origin
451 (method url-fetch)
452 (uri (string-append "https://forge.ocamlcore.org/frs/download.php/"
453 "1479/lablgtk-2.18.3.tar.gz"))
454 (sha256
455 (base32
456 "1bybn3jafxf4cx25zvn8h2xj9agn1xjbn7j3ywxxqx6az7rfnnwp"))))
457 (build-system gnu-build-system)
458 (native-inputs
459 `(("camlp4" ,camlp4)
460 ("ocaml" ,ocaml)
461 ("pkg-config" ,pkg-config)))
462 ;; FIXME: Add inputs gtkgl-2.0, libpanelapplet-2.0, gtkspell-2.0,
463 ;; and gtk+-quartz-2.0 once available.
464 (inputs
465 `(("gtk+" ,gtk+-2)
466 ("gtksourceview" ,gtksourceview-2)
467 ("libgnomecanvas" ,libgnomecanvas)
468 ("libgnomeui" ,libgnomeui)
469 ("libglade" ,libglade)
470 ("librsvg" ,librsvg)))
471 (arguments
472 `(#:tests? #f ; no check target
473
474 ;; Occasionally we would get "Error: Unbound module GtkThread" when
475 ;; compiling 'gtkThInit.ml', with 'make -j'. So build sequentially.
476 #:parallel-build? #f
477
478 #:phases
479 (modify-phases %standard-phases
480 (replace 'install
481 (lambda* (#:key inputs outputs #:allow-other-keys)
482 (let ((out (assoc-ref outputs "out"))
483 (ocaml (assoc-ref inputs "ocaml")))
484 ;; Install into the output and not the ocaml directory.
485 (substitute* "config.make"
486 ((ocaml) out))
487 (system* "make" "old-install")
488 #t))))))
489 (home-page "http://lablgtk.forge.ocamlcore.org/")
490 (synopsis "GTK+ bindings for OCaml")
491 (description
492 "LablGtk is an OCaml interface to GTK+ 1.2 and 2.x. It provides
493 a strongly-typed object-oriented interface that is compatible with the
494 dynamic typing of GTK+. Most widgets and methods are available. LablGtk
495 also provides bindings to
496 gdk-pixbuf, the GLArea widget (in combination with LablGL), gnomecanvas,
497 gnomeui, gtksourceview, gtkspell,
498 libglade (and it an generate OCaml code from .glade files),
499 libpanel, librsvg and quartz.")
500 (license lgpl2.1)))
501
502 (define-public unison
503 (package
504 (name "unison")
505 (version "2.48.3")
506 (source
507 (origin
508 (method svn-fetch)
509 (uri (svn-reference
510 (url (string-append "https://webdav.seas.upenn.edu/svn/"
511 "unison/branches/"
512 (version-major+minor version)))
513 (revision 535)))
514 (file-name (string-append name "-" version "-checkout"))
515 (sha256
516 (base32
517 "0486s53wyayicj9f2raj2dvwvk4xyzar219rccc1iczdwixm4x05"))
518 (modules '((guix build utils)
519 (ice-9 rdelim)
520 (ice-9 regex)
521 (srfi srfi-1)))
522 (snippet
523 `(begin
524 ;; The svn revision in the release tarball appears to be
525 ;; artificially manipulated in order to set the desired point
526 ;; version number. Because the point version is calculated during
527 ;; the build, we can offset pointVersionOrigin by the desired
528 ;; point version and write that into "Rev: %d". We do this rather
529 ;; than hardcoding the necessary revision number, for
530 ;; maintainability.
531 (with-atomic-file-replacement "src/mkProjectInfo.ml"
532 (lambda (in out)
533 (let ((pt-ver (string->number (third (string-split ,version #\.))))
534 (pt-rx (make-regexp "^let pointVersionOrigin = ([0-9]+)"))
535 (rev-rx (make-regexp "Rev: [0-9]+")))
536 (let loop ((pt-origin #f))
537 (let ((line (read-line in 'concat)))
538 (cond
539 ((regexp-exec pt-rx line)
540 => (lambda (m)
541 (display line out)
542 (loop (string->number (match:substring m 1)))))
543 ((regexp-exec rev-rx line)
544 => (lambda (m)
545 (format out "~aRev: ~d~a"
546 (match:prefix m)
547 (+ pt-origin pt-ver)
548 (match:suffix m))
549 (dump-port in out))) ;done
550 (else
551 (display line out)
552 (loop pt-origin))))))))
553 ;; Without the '-fix' argument, the html file produced does not
554 ;; have functioning internal hyperlinks.
555 (substitute* "doc/Makefile"
556 (("hevea unison") "hevea -fix unison"))))))
557 (build-system gnu-build-system)
558 (outputs '("out"
559 "doc")) ; 1.9 MiB of documentation
560 (native-inputs
561 `(("ocaml" ,ocaml)
562 ;; For documentation
563 ("ghostscript" ,ghostscript)
564 ("texlive" ,texlive)
565 ("hevea" ,hevea)
566 ("lynx" ,lynx)))
567 (arguments
568 `(#:parallel-build? #f
569 #:parallel-tests? #f
570 #:test-target "selftest"
571 #:tests? #f ; Tests require writing to $HOME.
572 ; If some $HOME is provided, they fail with the message
573 ; "Fatal error: Skipping some tests -- remove me!"
574 #:phases
575 (modify-phases %standard-phases
576 (delete 'configure)
577 (add-before 'install 'prepare-install
578 (lambda* (#:key outputs #:allow-other-keys)
579 (let* ((out (assoc-ref outputs "out"))
580 (bin (string-append out "/bin")))
581 (mkdir-p bin)
582 (setenv "HOME" out) ; forces correct INSTALLDIR in Makefile
583 #t)))
584 (add-after 'install 'install-doc
585 (lambda* (#:key inputs outputs #:allow-other-keys)
586 (let ((doc (string-append (assoc-ref outputs "doc")
587 "/share/doc/unison")))
588 (mkdir-p doc)
589 ;; This file needs write-permissions, because it's
590 ;; overwritten by 'docs' during documentation generation.
591 (chmod "src/strings.ml" #o600)
592 (and (zero? (system* "make" "docs"
593 "TEXDIRECTIVES=\\\\draftfalse"))
594 (begin
595 (for-each (lambda (f)
596 (install-file f doc))
597 (map (lambda (ext)
598 (string-append
599 "doc/unison-manual." ext))
600 ;; Install only html documentation,
601 ;; since the build is currently
602 ;; non-reproducible with the ps, pdf,
603 ;; and dvi docs.
604 '(;;"ps" "pdf" "dvi"
605 "html")))
606 #t))))))))
607 (home-page "https://www.cis.upenn.edu/~bcpierce/unison/")
608 (synopsis "File synchronizer")
609 (description
610 "Unison is a file-synchronization tool. It allows two replicas of
611 a collection of files and directories to be stored on different hosts
612 (or different disks on the same host), modified separately, and then
613 brought up to date by propagating the changes in each replica
614 to the other.")
615 (license gpl3+)))