summaryrefslogtreecommitdiff
path: root/guix
diff options
context:
space:
mode:
Diffstat (limited to 'guix')
-rw-r--r--guix/build-system/ocaml.scm27
-rw-r--r--guix/build/cargo-build-system.scm7
-rw-r--r--guix/cpio.scm33
-rw-r--r--guix/hg-download.scm38
-rw-r--r--guix/import/hackage.scm14
-rw-r--r--guix/import/stackage.scm8
-rw-r--r--guix/lint.scm83
-rw-r--r--guix/scripts/archive.scm11
-rw-r--r--guix/scripts/lint.scm15
-rw-r--r--guix/scripts/publish.scm6
-rw-r--r--guix/scripts/refresh.scm2
-rw-r--r--guix/scripts/system.scm21
-rw-r--r--guix/store.scm7
-rw-r--r--guix/utils.scm6
14 files changed, 254 insertions, 24 deletions
diff --git a/guix/build-system/ocaml.scm b/guix/build-system/ocaml.scm
index c5996bf0cf..5513216c25 100644
--- a/guix/build-system/ocaml.scm
+++ b/guix/build-system/ocaml.scm
@@ -29,6 +29,8 @@
#:export (%ocaml-build-system-modules
package-with-ocaml4.07
strip-ocaml4.07-variant
+ package-with-ocaml4.09
+ strip-ocaml4.09-variant
default-findlib
default-ocaml
lower
@@ -96,6 +98,18 @@
(let ((module (resolve-interface '(gnu packages ocaml))))
(module-ref module 'ocaml4.07-dune)))
+(define (default-ocaml4.09)
+ (let ((ocaml (resolve-interface '(gnu packages ocaml))))
+ (module-ref ocaml 'ocaml-4.09)))
+
+(define (default-ocaml4.09-findlib)
+ (let ((module (resolve-interface '(gnu packages ocaml))))
+ (module-ref module 'ocaml4.09-findlib)))
+
+(define (default-ocaml4.09-dune)
+ (let ((module (resolve-interface '(gnu packages ocaml))))
+ (module-ref module 'ocaml4.09-dune)))
+
(define* (package-with-explicit-ocaml ocaml findlib dune old-prefix new-prefix
#:key variant-property)
"Return a procedure of one argument, P. The procedure creates a package
@@ -171,6 +185,19 @@ pre-defined variants."
(inherit p)
(properties (alist-delete 'ocaml4.07-variant (package-properties p)))))
+(define package-with-ocaml4.09
+ (package-with-explicit-ocaml (delay (default-ocaml4.09))
+ (delay (default-ocaml4.09-findlib))
+ (delay (default-ocaml4.09-dune))
+ "ocaml-" "ocaml4.09-"
+ #:variant-property 'ocaml4.09-variant))
+
+(define (strip-ocaml4.09-variant p)
+ "Remove the 'ocaml4.09-variant' property from P."
+ (package
+ (inherit p)
+ (properties (alist-delete 'ocaml4.09-variant (package-properties p)))))
+
(define* (lower name
#:key source inputs native-inputs outputs system target
(ocaml (default-ocaml))
diff --git a/guix/build/cargo-build-system.scm b/guix/build/cargo-build-system.scm
index 117c8da66c..a638d0eded 100644
--- a/guix/build/cargo-build-system.scm
+++ b/guix/build/cargo-build-system.scm
@@ -122,6 +122,13 @@ directory = '" port)
(setenv "CC" (string-append (assoc-ref inputs "gcc") "/bin/gcc"))
(setenv "LIBGIT2_SYS_USE_PKG_CONFIG" "1")
(setenv "LIBSSH2_SYS_USE_PKG_CONFIG" "1")
+ (when (assoc-ref inputs "openssl")
+ (setenv "OPENSSL_DIR" (assoc-ref inputs "openssl")))
+ (when (assoc-ref inputs "gettext")
+ (setenv "GETTEXT_SYSTEM" (assoc-ref inputs "gettext")))
+ (when (assoc-ref inputs "clang")
+ (setenv "LIBCLANG_PATH"
+ (string-append (assoc-ref inputs "clang") "/lib")))
;; We don't use the Cargo.lock file to determine the package versions we use
;; during building, and in any case if one is not present it is created
diff --git a/guix/cpio.scm b/guix/cpio.scm
index e4692e2e9c..c9932f5bf9 100644
--- a/guix/cpio.scm
+++ b/guix/cpio.scm
@@ -27,6 +27,7 @@
make-cpio-header
file->cpio-header
file->cpio-header*
+ special-file->cpio-header*
write-cpio-header
read-cpio-header
@@ -132,9 +133,10 @@
(%make-cpio-header MAGIC
inode mode uid gid
nlink mtime
- (if (= C_ISDIR (logand mode C_FMT))
- 0
- size)
+ (if (or (= C_ISLNK (logand mode C_FMT))
+ (= C_ISREG (logand mode C_FMT)))
+ size
+ 0)
major minor rmajor rminor
(+ name-size 1) ;include trailing zero
0))) ;checksum
@@ -146,6 +148,8 @@ denotes, similar to 'stat:type'."
(cond ((= C_ISREG fmt) 'regular)
((= C_ISDIR fmt) 'directory)
((= C_ISLNK fmt) 'symlink)
+ ((= C_ISBLK fmt) 'block-special)
+ ((= C_ISCHR fmt) 'char-special)
(else
(error "unsupported file type" mode)))))
@@ -187,6 +191,25 @@ produced in a deterministic fashion."
#:size (stat:size st)
#:name-size (string-length file-name))))
+(define* (special-file->cpio-header* file
+ device-type
+ device-major
+ device-minor
+ permission-bits
+ #:optional (file-name file))
+ "Create a character or block device header.
+
+DEVICE-TYPE is either 'char-special or 'block-special.
+
+The number of hard links is assumed to be 1."
+ (make-cpio-header #:mode (logior (match device-type
+ ('block-special C_ISBLK)
+ ('char-special C_ISCHR))
+ permission-bits)
+ #:nlink 1
+ #:rdev (device-number device-major device-minor)
+ #:name-size (string-length file-name)))
+
(define %trailer
"TRAILER!!!")
@@ -233,6 +256,10 @@ produces with the '-H newc' option."
(put-string port target)))
((directory)
#t)
+ ((block-special)
+ #t)
+ ((char-special)
+ #t)
(else
(error "file type not supported")))
diff --git a/guix/hg-download.scm b/guix/hg-download.scm
index 694105ceba..bd55946523 100644
--- a/guix/hg-download.scm
+++ b/guix/hg-download.scm
@@ -26,12 +26,14 @@
#:use-module (guix packages)
#:autoload (guix build-system gnu) (standard-packages)
#:use-module (ice-9 match)
+ #:use-module (ice-9 popen)
+ #:use-module (ice-9 rdelim)
#:export (hg-reference
hg-reference?
hg-reference-url
hg-reference-changeset
hg-reference-recursive?
-
+ hg-predicate
hg-fetch))
;;; Commentary:
@@ -93,4 +95,38 @@ HASH-ALGO (a symbol). Use NAME as the file name, or a generic name if #f."
#:recursive? #t
#:guile-for-build guile)))
+(define (hg-file-list directory)
+ "Evaluates to a list of files contained in the repository at path
+ @var{directory}"
+ (let* ((port (open-input-pipe (format #f "hg files --repository ~s" directory)))
+ (files (let loop ((files '()))
+ (let ((line (read-line port)))
+ (cond
+ ((eof-object? line) files)
+ (else
+ (loop (cons line files))))))))
+ (close-pipe port)
+ (map canonicalize-path files)))
+
+(define (should-select? path-list candidate)
+ "Returns #t in case that @var{candidate} is a file that is part of the given
+@var{path-list}."
+ (let ((canon-candidate (canonicalize-path candidate)))
+ (let loop ((xs path-list))
+ (cond
+ ((null? xs)
+ ;; Directories are not part of `hg files', but `local-file' will not
+ ;; recurse if we don't return #t for directories.
+ (equal? (array-ref (lstat candidate) 13) 'directory))
+ ((string-contains candidate (car xs)) #t)
+ (else (loop (cdr xs)))))))
+
+(define (hg-predicate directory)
+ "This procedure evaluates to a predicate that reports back whether a given
+@var{file} - @var{stat} combination is part of the files tracked by
+Mercurial."
+ (let ((files (hg-file-list directory)))
+ (lambda (file stat)
+ (should-select? files file))))
+
;;; hg-download.scm ends here
diff --git a/guix/import/hackage.scm b/guix/import/hackage.scm
index 35c67cad8d..6ca4f65cb0 100644
--- a/guix/import/hackage.scm
+++ b/guix/import/hackage.scm
@@ -40,7 +40,8 @@
#:use-module (guix upstream)
#:use-module (guix packages)
#:use-module ((guix utils) #:select (call-with-temporary-output-file))
- #:export (hackage->guix-package
+ #:export (%hackage-url
+ hackage->guix-package
hackage-recursive-import
%hackage-updater
@@ -92,20 +93,23 @@
(define package-name-prefix "ghc-")
+(define %hackage-url
+ (make-parameter "https://hackage.haskell.org"))
+
(define (hackage-source-url name version)
"Given a Hackage package NAME and VERSION, return a url to the source
tarball."
- (string-append "https://hackage.haskell.org/package/" name
- "/" name "-" version ".tar.gz"))
+ (string-append (%hackage-url) "/package/"
+ name "/" name "-" version ".tar.gz"))
(define* (hackage-cabal-url name #:optional version)
"Given a Hackage package NAME and VERSION, return a url to the corresponding
.cabal file on Hackage. If VERSION is #f or missing, the url for the latest
version is returned."
(if version
- (string-append "https://hackage.haskell.org/package/"
+ (string-append (%hackage-url) "/package/"
name "-" version "/" name ".cabal")
- (string-append "https://hackage.haskell.org/package/"
+ (string-append (%hackage-url) "/package/"
name "/" name ".cabal")))
(define (hackage-name->package-name name)
diff --git a/guix/import/stackage.scm b/guix/import/stackage.scm
index 93cf214127..77cc6350cb 100644
--- a/guix/import/stackage.scm
+++ b/guix/import/stackage.scm
@@ -30,7 +30,8 @@
#:use-module (guix memoization)
#:use-module (guix packages)
#:use-module (guix upstream)
- #:export (stackage->guix-package
+ #:export (%stackage-url
+ stackage->guix-package
stackage-recursive-import
%stackage-updater))
@@ -39,7 +40,8 @@
;;; Stackage info fetcher and access functions
;;;
-(define %stackage-url "https://www.stackage.org")
+(define %stackage-url
+ (make-parameter "https://www.stackage.org"))
;; Latest LTS version compatible with GHC 8.6.5.
(define %default-lts-version "14.27")
@@ -55,7 +57,7 @@
;; "Retrieve the information about the LTS Stackage release VERSION."
(memoize
(lambda* (#:optional (version ""))
- (let* ((url (string-append %stackage-url
+ (let* ((url (string-append (%stackage-url)
"/lts-" (if (string-null? version)
%default-lts-version
version)))
diff --git a/guix/lint.scm b/guix/lint.scm
index 91dbc806dc..be6bb4eb01 100644
--- a/guix/lint.scm
+++ b/guix/lint.scm
@@ -10,6 +10,7 @@
;;; Copyright © 2017, 2018, 2020 Efraim Flashner <efraim@flashner.co.il>
;;; Copyright © 2018, 2019 Arun Isaac <arunisaac@systemreboot.net>
;;; Copyright © 2020 Chris Marusich <cmmarusich@gmail.com>
+;;; Copyright © 2020 Timothy Sample <samplet@ngyro.com>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -35,6 +36,8 @@
#:use-module (guix http-client)
#:use-module (guix packages)
#:use-module (guix i18n)
+ #:use-module ((guix gexp)
+ #:select (local-file? local-file-absolute-file-name))
#:use-module (guix licenses)
#:use-module (guix records)
#:use-module (guix grafts)
@@ -50,6 +53,7 @@
#:use-module ((guix swh) #:hide (origin?))
#:autoload (guix git-download) (git-reference?
git-reference-url git-reference-commit)
+ #:use-module (guix import stackage)
#:use-module (ice-9 match)
#:use-module (ice-9 regex)
#:use-module (ice-9 format)
@@ -73,6 +77,7 @@
check-inputs-should-be-native
check-inputs-should-not-be-an-input-at-all
check-patch-file-names
+ check-patch-headers
check-synopsis-style
check-derivation
check-home-page
@@ -87,6 +92,7 @@
check-formatting
check-archival
check-profile-collisions
+ check-haskell-stackage
lint-warning
lint-warning?
@@ -712,6 +718,54 @@ patch could not be found."
(_ #f))
patches)))))
+(define (check-patch-headers package)
+ "Check that PACKAGE's patches start with a comment. Return a list of
+warnings."
+ (define (blank? str)
+ (string-every char-set:blank str))
+
+ (define (patch-header-warnings patch)
+ (call-with-input-file patch
+ (lambda (port)
+ ;; Read from PORT until a non-blank line is found or EOF is reached.
+ (let loop ()
+ (let ((line (read-line port)))
+ (cond ((eof-object? line)
+ (list (make-warning package
+ (G_ "~a: empty patch")
+ (list (basename patch))
+ #:field 'source)))
+ ((blank? line)
+ (loop))
+ ((or (string-prefix? "--- " line)
+ (string-prefix? "+++ " line))
+ (list (make-warning package
+ (G_ "~a: patch lacks comment and \
+upstream status")
+ (list (basename patch))
+ #:field 'source)))
+ (else
+ '())))))))
+
+ (guard (c ((formatted-message? c) ;raised by 'search-patch'
+ (list (%make-warning package
+ (formatted-message-string c)
+ (formatted-message-arguments c)
+ #:field 'source))))
+ (let ((patches (if (origin? (package-source package))
+ (origin-patches (package-source package))
+ '())))
+ (append-map (lambda (patch)
+ ;; Dismiss PATCH if it's an origin or similar.
+ (cond ((string? patch)
+ (patch-header-warnings patch))
+ ((local-file? patch)
+ (patch-header-warnings
+ (local-file-absolute-file-name patch)))
+ (else
+ '())))
+ patches))))
+
(define (escape-quotes str)
"Replace any quote character in STR by an escaped quote character."
(list->string
@@ -1234,6 +1288,25 @@ Heritage")
'()
(apply throw key args))))))))
+(define (check-haskell-stackage package)
+ "Check whether PACKAGE is a Haskell package ahead of the current
+Stackage LTS version."
+ (match (with-networking-fail-safe
+ (format #f (G_ "while retrieving upstream info for '~a'")
+ (package-name package))
+ #f
+ (package-latest-release package (list %stackage-updater)))
+ ((? upstream-source? source)
+ (if (version>? (package-version package)
+ (upstream-source-version source))
+ (list
+ (make-warning package
+ (G_ "ahead of Stackage LTS version ~a")
+ (list (upstream-source-version source))
+ #:field 'version))
+ '()))
+ (#f '())))
+
;;;
;;; Source code formatting.
@@ -1418,6 +1491,10 @@ or a list thereof")
(description "Validate file names and availability of patches")
(check check-patch-file-names))
(lint-checker
+ (name 'patch-headers)
+ (description "Validate patch headers")
+ (check check-patch-headers))
+ (lint-checker
(name 'formatting)
(description "Look for formatting issues in the source")
(check check-formatting))))
@@ -1456,7 +1533,11 @@ or a list thereof")
(lint-checker
(name 'archival)
(description "Ensure source code archival on Software Heritage")
- (check check-archival))))
+ (check check-archival))
+ (lint-checker
+ (name 'haskell-stackage)
+ (description "Ensure Haskell packages use Stackage LTS versions")
+ (check check-haskell-stackage))))
(define %all-checkers
(append %local-checkers
diff --git a/guix/scripts/archive.scm b/guix/scripts/archive.scm
index 02557ce454..c04baf9784 100644
--- a/guix/scripts/archive.scm
+++ b/guix/scripts/archive.scm
@@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2020 Tobias Geerinckx-Rice <me@tobias.gr>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -310,6 +311,16 @@ the input port."
(leave (G_ "failed to read public key: ~a: ~a~%")
(error-source err) (error-string err)))))
+ ;; Warn about potentially volatile ACLs, but continue: system reconfiguration
+ ;; might not be possible without (newly-authorized) substitutes.
+ (let ((stat (false-if-exception (lstat %acl-file))))
+ (when (and stat (eq? 'symlink (stat:type (lstat %acl-file))))
+ (warning (G_ "replacing symbolic link ~a with a regular file~%")
+ %acl-file)
+ (when (string-prefix? (%store-prefix) (readlink %acl-file))
+ (display-hint (G_ "On Guix System, add public keys to the
+@code{authorized-keys} field of your @code{operating-system} instead.")))))
+
(let ((key (read-key))
(acl (current-acl)))
(unless (eq? 'public-key (canonical-sexp-nth-data key 0))
diff --git a/guix/scripts/lint.scm b/guix/scripts/lint.scm
index 18cd167537..c72dc3caad 100644
--- a/guix/scripts/lint.scm
+++ b/guix/scripts/lint.scm
@@ -10,6 +10,7 @@
;;; Copyright © 2017, 2018 Efraim Flashner <efraim@flashner.co.il>
;;; Copyright © 2018, 2019 Arun Isaac <arunisaac@systemreboot.net>
;;; Copyright © 2019, 2020 Simon Tournier <zimon.toutoune@gmail.com>
+;;; Copyright © 2020 Brice Waegeneire <brice@waegenei.re>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -47,11 +48,15 @@
;; provided MESSAGE.
(for-each
(lambda (lint-warning)
- (let ((package (lint-warning-package lint-warning))
- (loc (lint-warning-location lint-warning)))
- (info loc (G_ "~a@~a: ~a~%")
- (package-name package) (package-version package)
- (lint-warning-message lint-warning))))
+ (let* ((package (lint-warning-package lint-warning))
+ (name (package-name package))
+ (version (package-version package))
+ (loc (lint-warning-location lint-warning))
+ (message (lint-warning-message lint-warning)))
+ (parameterize
+ ((guix-warning-port (current-output-port)))
+ (info loc (G_ "~a@~a: ~a~%")
+ name version message))))
warnings))
(define* (run-checkers package checkers #:key store)
diff --git a/guix/scripts/publish.scm b/guix/scripts/publish.scm
index f1a9970a7f..2a2185e2b9 100644
--- a/guix/scripts/publish.scm
+++ b/guix/scripts/publish.scm
@@ -63,10 +63,14 @@
#:use-module ((guix build utils)
#:select (dump-port mkdir-p find-files))
#:use-module ((guix build syscalls) #:select (set-thread-name))
- #:export (%public-key
+ #:export (%default-gzip-compression
+
+ %public-key
%private-key
signed-string
+ open-server-socket
+ run-publish-server
guix-publish))
(define (show-help)
diff --git a/guix/scripts/refresh.scm b/guix/scripts/refresh.scm
index 4a71df28d1..fb6c52a567 100644
--- a/guix/scripts/refresh.scm
+++ b/guix/scripts/refresh.scm
@@ -440,7 +440,7 @@ releases for ~a~%")
(full-name x)))
(lst
(format (current-output-port)
- (N_ "Building the following ~*package would ensure ~d \
+ (N_ "Building the following ~d package would ensure ~d \
dependent packages are rebuilt: ~{~a~^ ~}~%"
"Building the following ~d packages would ensure ~d \
dependent packages are rebuilt: ~{~a~^ ~}~%"
diff --git a/guix/scripts/system.scm b/guix/scripts/system.scm
index ad998156c2..db80e0be8f 100644
--- a/guix/scripts/system.scm
+++ b/guix/scripts/system.scm
@@ -674,7 +674,8 @@ checking this by themselves in their 'check' procedure."
(define* (system-derivation-for-action os action
#:key image-size image-type
full-boot? container-shared-network?
- mappings label)
+ mappings label
+ volatile-root?)
"Return as a monadic value the derivation for OS according to ACTION."
(mlet %store-monad ((target (current-target-system)))
(case action
@@ -706,7 +707,8 @@ checking this by themselves in their 'check' procedure."
base-image))
(target (or base-target target))
(size image-size)
- (operating-system os))))))
+ (operating-system os)
+ (volatile-root? volatile-root?))))))
((docker-image)
(system-docker-image os
#:shared-network? container-shared-network?)))))
@@ -761,6 +763,7 @@ and TARGET arguments."
dry-run? derivations-only?
use-substitutes? bootloader-target target
image-size image-type
+ volatile-root?
full-boot? label container-shared-network?
(mappings '())
(gc-root #f))
@@ -768,7 +771,8 @@ and TARGET arguments."
bootloader; BOOTLOADER-TAGET is the target for the bootloader; TARGET is the
target root directory; IMAGE-SIZE is the size of the image to be built, for
the 'vm-image' and 'disk-image' actions. IMAGE-TYPE is the type of image to
-be built.
+be built. When VOLATILE-ROOT? is #t, the root file system is mounted
+volatile.
FULL-BOOT? is used for the 'vm' action; it determines whether to
boot directly to the kernel or to the bootloader. CONTAINER-SHARED-NETWORK?
@@ -816,6 +820,7 @@ static checks."
#:label label
#:image-type image-type
#:image-size image-size
+ #:volatile-root? volatile-root?
#:full-boot? full-boot?
#:container-shared-network? container-shared-network?
#:mappings mappings))
@@ -975,6 +980,8 @@ Some ACTIONS support additional ARGS.\n"))
(display (G_ "
--no-bootloader for 'init', do not install a bootloader"))
(display (G_ "
+ --volatile for 'disk-image', make the root file system volatile"))
+ (display (G_ "
--label=LABEL for 'disk-image', label disk image with LABEL"))
(display (G_ "
--save-provenance save provenance information"))
@@ -1048,6 +1055,9 @@ Some ACTIONS support additional ARGS.\n"))
(option '("no-bootloader" "no-grub") #f #f
(lambda (opt name arg result)
(alist-cons 'install-bootloader? #f result)))
+ (option '("volatile") #f #f
+ (lambda (opt name arg result)
+ (alist-cons 'volatile-root? #t result)))
(option '("label") #t #f
(lambda (opt name arg result)
(alist-cons 'label arg result)))
@@ -1109,7 +1119,8 @@ Some ACTIONS support additional ARGS.\n"))
(image-type . raw)
(image-size . guess)
(install-bootloader? . #t)
- (label . #f)))
+ (label . #f)
+ (volatile-root? . #f)))
(define (verbosity-level opts)
"Return the verbosity level based on OPTS, the alist of parsed options."
@@ -1206,6 +1217,8 @@ resulting from command-line parsing."
#:image-type (lookup-image-type-by-name
(assoc-ref opts 'image-type))
#:image-size (assoc-ref opts 'image-size)
+ #:volatile-root?
+ (assoc-ref opts 'volatile-root?)
#:full-boot? (assoc-ref opts 'full-boot?)
#:container-shared-network?
(assoc-ref opts 'container-shared-network?)
diff --git a/guix/store.scm b/guix/store.scm
index d859ea33ed..4da39971b5 100644
--- a/guix/store.scm
+++ b/guix/store.scm
@@ -113,6 +113,7 @@
build
query-failed-paths
clear-failed-paths
+ ensure-path
add-temp-root
add-indirect-root
add-permanent-root
@@ -1397,6 +1398,12 @@ When a handler is installed with 'with-build-handler', it is called any time
(message "unsupported build mode")
(status 1))))))))))))
+(define-operation (ensure-path (store-path path))
+ "Ensure that a path is valid. If it is not valid, it may be made valid by
+running a substitute. As a GC root is not created by the daemon, you may want
+to call ADD-TEMP-ROOT on that store path."
+ boolean)
+
(define-operation (add-temp-root (store-path path))
"Make PATH a temporary root for the duration of the current session.
Return #t."
diff --git a/guix/utils.scm b/guix/utils.scm
index b816c355dc..a591b62f30 100644
--- a/guix/utils.scm
+++ b/guix/utils.scm
@@ -78,6 +78,7 @@
target-arm?
target-64bit?
cc-for-target
+ cxx-for-target
version-compare
version>?
@@ -542,6 +543,11 @@ a character other than '@'."
(string-append target "-gcc")
"gcc"))
+(define* (cxx-for-target #:optional (target (%current-target-system)))
+ (if target
+ (string-append target "-g++")
+ "g++"))
+
(define version-compare
(let ((strverscmp
(let ((sym (or (dynamic-func "strverscmp" (dynamic-link))