Commit ac013b1b authored by Robert Goldman's avatar Robert Goldman
Browse files

Merge branch 'doc-update' into 'master'

Doc update

I'm slowly proofreading the code of ASDF and adding more comments and docstrings. That will take a few weeks to complete. Here is what I have so far. You may merge at any point. This branch ought to not modify any code as such, only comments, docstrings, manual sections, and other non-code-generating parts of the repository.

See merge request !24
parents 67172768 817e6ffd
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -4,8 +4,9 @@
** Already, swank-asdf doesn't support anything older than 2.014.6
   (as provided by the first 2011 iteration of Quicklisp), and
   I (fare) am pushing for swank-asdf to not support anything older
   than 3.0 (since no maintained implementation has anything older,
   with the LispWorks 7.0 release being last to upgrade, in May 2015).
   than 3.1.2 (since no maintained implementation has anything older,
   with the LispWorks 7.0 release being last to upgrade, in May 2015;
   or CLISP hg in May 2016, though it hasn't been released since 2010).
** Maybe remove old versions from upgrade tests; or maybe not:
   if we support old versions in any way,
   it should be for upgrade only, and "punt" is acceptable.
@@ -13,6 +14,8 @@
   Clear all the code in the header, and/or
   move a few elements to asdf/upgrade.
** Upgrade tests might be split in two test sets: short and long.
** Make tests stateless and/or make any modifications private,
   so that they can be run in parallel.

* Moving toward cross-compilation (ASDF 3.2? 4?)
   This would allow to get most of the benefits of XCVB
+78 −25
Original line number Diff line number Diff line
@@ -22,29 +22,49 @@
(in-package :asdf/action)

(eval-when (#-lispworks :compile-toplevel :load-toplevel :execute) ;; LispWorks issues spurious warning
  (deftype action () '(cons operation component)) ;; a step to be performed while building

  (deftype action ()
    "A pair of operation and component uniquely identifies a node in the dependency graph
of steps to be performed while building a system."
    '(cons operation component))

  (deftype operation-designator ()
    ;; an operation designates itself,
    ;; nil designates a context-dependent current operation, and
    ;; class-name or class designates an instance of the designated class.
    "An operation designates itself. NIL designates a context-dependent current operation,
and a class-name or class designates the canonical instance of the designated class."
    '(or operation null symbol class)))


;;; TODO: These should be moved to asdf/plan and be made simple defuns.
(with-upgradability ()
  (defgeneric traverse-actions (actions &key &allow-other-keys))
  (defgeneric traverse-sub-actions (operation component &key &allow-other-keys))
  (defgeneric required-components (component &key &allow-other-keys)))

;;;; Reified representation for storage or debugging. Note: dropping original-initargs

;;;; Reified representation for storage or debugging. Note: it drops the operation-original-initargs
(with-upgradability ()
  (defun action-path (action)
    "A readable data structure that identifies the action."
    (destructuring-bind (o . c) action (cons (type-of o) (component-find-path c))))
  (defun find-action (path)
    "Reconstitute an action from its action-path"
    (destructuring-bind (o . c) path (cons (make-operation o) (find-component () c)))))


;;;; Convenience methods
(with-upgradability ()
  ;; A macro that defines convenience methods for a generic function (gf) that
  ;; dispatches on operation and component.  The convenience methods allow users
  ;; to call the gf with operation and/or component designators, that the
  ;; methods will resolve into actual operation and component objects, so that
  ;; the users can interact using readable designators, but developers only have
  ;; to write methods that handle operation and component objects.
  ;; FUNCTION is the generic function name
  ;; FORMALS is its list of arguments, which must include OPERATION and COMPONENT.
  ;; IF-NO-OPERATION is a form (defaults to NIL) describing what to do if no operation is found.
  ;; IF-NO-COMPONENT is a form (defaults to NIL) describing what to do if no component is found.
  ;; If OPERATION-INITARGS is true, then for backward compatibility the function has
  ;; a &rest argument that is passed into the operation's initargs if and when it is created.
  (defmacro define-convenience-action-methods
      (function formals &key if-no-operation if-no-component operation-initargs)
    (let* ((rest (gensym "REST"))
@@ -94,12 +114,18 @@ You can put together sentences using this phrase."))
  (defmethod action-description (operation component)
    (format nil (compatfmt "~@<~A on ~A~@:>")
            (type-of operation) component))
  (defgeneric* (explain) (operation component))

  ;; This is for compatibility with ASDF 1, and is deprecated.
  ;; TODO: move it to backward-interface
  (defgeneric* (explain) (operation component)
    (:documentation "Display a message describing an action"))
  (defmethod explain ((o operation) (c component))
    (asdf-message (compatfmt "~&~@<; ~@;~A~:>~%") (action-description o c)))
  (define-convenience-action-methods explain (operation component))

  (defun format-action (stream action &optional colon-p at-sign-p)
    "FORMAT helper to display an action's action-description.
Use it in FORMAT control strings as ~/asdf-action:format-action/"
    (assert (null colon-p)) (assert (null at-sign-p))
    (destructuring-bind (operation . component) action
      (princ (action-description operation component) stream))))
@@ -260,10 +286,22 @@ The class needs to be updated for ASDF 3.1 and specify appropriate propagation m

;;;; Inputs, Outputs, and invisible dependencies
(with-upgradability ()
  (defgeneric* (output-files) (operation component))
  (defgeneric* (input-files) (operation component))
  (defgeneric* (output-files) (operation component)
    (:documentation "Methods for this function return two values: a list of output files
corresponding to this action, and a boolean indicating if they have already been subjected
to relevant output translations and should not be further translated.

Methods on PERFORM *must* call this function to determine where their outputs are to be located.
They may rely on the order of the files to discriminate between outputs.
"))
  (defgeneric* (input-files) (operation component)
    (:documentation "A list of input files corresponding to this action.

Methods on PERFORM *must* call this function to determine where their inputs are located.
They may rely on the order of the files to discriminate between inputs.
"))
  (defgeneric* (operation-done-p) (operation component)
    (:documentation "Returns a boolean, which is NIL if the action is forced to be performed again"))
    (:documentation "Returns a boolean which is NIL if the action must be performed (again)."))
  (define-convenience-action-methods output-files (operation component))
  (define-convenience-action-methods input-files (operation component))
  (define-convenience-action-methods operation-done-p (operation component))
@@ -271,8 +309,8 @@ The class needs to be updated for ASDF 3.1 and specify appropriate propagation m
  (defmethod operation-done-p ((o operation) (c component))
    t)

  ;; Translate output files, unless asked not to. Memoize the result.
  (defmethod output-files :around (operation component)
    "Translate output files, unless asked not to. Memoize the result."
    operation component ;; hush genera, not convinced by declare ignorable(!)
    (do-asdf-cache `(output-files ,operation ,component)
      (values
@@ -297,14 +335,19 @@ The class needs to be updated for ASDF 3.1 and specify appropriate propagation m
      (assert (length=n-p files 1))
      (first files)))

  ;; Memoize input files.
  (defmethod input-files :around (operation component)
    "memoize input files."
    (do-asdf-cache `(input-files ,operation ,component)
      (call-next-method)))

  ;; By default an action has no input-files.
  (defmethod input-files ((o operation) (c component))
    nil)

  ;; An action with a selfward-operation by default gets its input-files from the output-files of
  ;; the actions using selfward-operations it depends on (and the same component),
  ;; or if there are none, on the component-pathname of the component if it's a file
  ;; -- and then on the results of the next-method.
  (defmethod input-files ((o selfward-operation) (c component))
    `(,@(or (loop :for dep-o :in (ensure-list (selfward-operation o))
                  :append (or (output-files dep-o c) (input-files dep-o c)))
@@ -315,23 +358,31 @@ The class needs to be updated for ASDF 3.1 and specify appropriate propagation m

;;;; Done performing
(with-upgradability ()
  (defgeneric component-operation-time (operation component)) ;; ASDF4: hide it behind plan-action-stamp
  (defgeneric (setf component-operation-time) (time operation component))
  ;; ASDF4: hide it behind plan-action-stamp
  (defgeneric component-operation-time (operation component)
    (:documentation "Return the timestamp for when an action was last performed"))
  (defgeneric (setf component-operation-time) (time operation component)
    (:documentation "Update the timestamp for when an action was last performed"))
  (define-convenience-action-methods component-operation-time (operation component))

  (defgeneric mark-operation-done (operation component)) ;; ASDF4: hide it behind (setf plan-action-stamp)
  ;; ASDF4: hide it behind (setf plan-action-stamp)
  (defgeneric mark-operation-done (operation component)
    (:documentation "Mark a action as having been just done.

Updates the action's COMPONENT-OPERATION-TIME to match the COMPUTE-ACTION-STAMP
using the JUST-DONE flag."))
  (defgeneric compute-action-stamp (plan operation component &key just-done)
    (:documentation "Has this action been successfully done already,
and at what known timestamp has it been done at or will it be done at?
Takes two keywords JUST-DONE and PLAN:
JUST-DONE is a boolean that is true if the action was just successfully performed,
* JUST-DONE is a boolean that is true if the action was just successfully performed,
  at which point we want compute the actual stamp and warn if files are missing;
  otherwise we are making plans, anticipating the effects of the action.
PLAN is a plan object modelling future effects of actions,
* PLAN is a plan object modelling future effects of actions,
  or NIL to denote what actually happened.
Returns two values:
* a STAMP saying when it was done or will be done,
  or T if the action has involves files that need to be recomputed.
  or T if the action involves files that need to be recomputed.
* a boolean DONE-P that indicates whether the action has actually been done,
  and both its output-files and its in-image side-effects are up to date."))

@@ -362,8 +413,8 @@ in some previous image, or T if it needs to be done.")

;;;; Perform
(with-upgradability ()
  (defgeneric* (perform-with-restarts) (operation component))
  (defgeneric* (perform) (operation component))
  (defgeneric* (perform) (operation component)
    (:documentation "PERFORM an action, consuming its input-files and building its output-files"))
  (define-convenience-action-methods perform (operation component))

  (defmethod perform :before ((o operation) (c component))
@@ -380,10 +431,12 @@ in some previous image, or T if it needs to be done.")
       (compatfmt "~@<Required method ~S not implemented for ~/asdf-action:format-action/~@:>")
       'perform (cons o c))))

  ;; The restarts of the perform-with-restarts variant matter in an interactive context.
  ;; The retry strategies of p-w-r itself, and/or the background workers of a multiprocess build
  ;; may call perform directly rather than call p-w-r.
  (defgeneric* (perform-with-restarts) (operation component)
    (:documentation "PERFORM an action in a context where suitable restarts are in place."))
  (defmethod perform-with-restarts (operation component)
    ;; TOO verbose, especially as the default. Add your own :before method
    ;; to perform-with-restart or perform if you want that:
    #|(explain operation component)|#
    (perform operation component))
  (defmethod perform-with-restarts :around (operation component)
    (loop
+39 −11
Original line number Diff line number Diff line
@@ -20,6 +20,10 @@
(in-package :asdf/backward-interface)

(with-upgradability ()
  ;; These conditions from ASDF 1 and 2 are used by many packages in Quicklisp;
  ;; but ASDF3 replaced them with somewhat different variants of uiop:compile-condition
  ;; that do not involve ASDF actions.
  ;; TODO: find the offenders and stop them.
  (define-condition operation-error (error) ;; Bad, backward-compatible name
    ;; Used by SBCL, cffi-tests, clsql-mysql, clsql-uffi, qt, elephant, uffi-tests, sb-grovel
    ((component :reader error-component :initarg :component)
@@ -32,16 +36,37 @@
  (define-condition compile-warned (compile-error) ())

  (defun component-load-dependencies (component)
    "DEPRECATED. Please use COMPONENT-SIDEWAY-DEPENDENCIES instead."
    ;; Old deprecated name for the same thing. Please update your software.
    (component-sideway-dependencies component))

  (defgeneric operation-forced (operation)) ;; Used by swank.asd for swank-loader.
  (defgeneric operation-forced (operation)
    (:documentation "DEPRECATED. Assume it's (constantly t) instead."))
  ;; This method exists for backward compatibility with swank.asd, its only user,
  ;; that still uses it as of 2016-09-21.
  ;;
  ;; The magic PERFORM method in swank.asd only actually loads swank if it sees that
  ;; the operation was forced. But except for the first time, the only reason the action
  ;; would be performed to begin with is because it was forced; and the first time over,
  ;; it doesn't hurt that :reload t :delete t should be used. So the check is redundant.
  ;; More generally, if you have to do something when the operation was forced,
  ;; you should also do it when not, and vice-versa, because it really shouldn't matter.
  ;; Thus, the backward-compatible thing to do is to always return T.
  ;;
  ;; TODO: change this function to a defun that always returns T.
  (defmethod operation-forced ((o operation)) (getf (operation-original-initargs o) :force))

  (defgeneric operation-on-warnings (operation))
  (defgeneric operation-on-failure (operation))
  (defgeneric (setf operation-on-warnings) (x operation))
  (defgeneric (setf operation-on-failure) (x operation))

  ;; These old interfaces from ASDF1 have never been very meaningful
  ;; but are still used in obscure places.
  (defgeneric operation-on-warnings (operation)
    (:documentation "DEPRECATED. Please use UIOP:*COMPILE-FILE-WARNINGS-BEHAVIOUR* instead."))
  (defgeneric operation-on-failure (operation)
    (:documentation "DEPRECATED. Please use UIOP:*COMPILE-FILE-FAILURE-BEHAVIOUR* instead."))
  (defgeneric (setf operation-on-warnings) (x operation)
    (:documentation "DEPRECATED. Please SETF UIOP:*COMPILE-FILE-WARNINGS-BEHAVIOUR* instead."))
  (defgeneric (setf operation-on-failure) (x operation)
    (:documentation "DEPRECATED. Please SETF UIOP:*COMPILE-FILE-FAILURE-BEHAVIOUR* instead."))
  (defmethod operation-on-warnings ((o operation))
    *compile-file-warnings-behaviour*)
  (defmethod operation-on-failure ((o operation))
@@ -55,15 +80,17 @@
    ;; As of 2.014.8, we mean to make this function obsolete,
    ;; but that won't happen until all clients have been updated.
    ;;(cerror "Use ASDF:SYSTEM-SOURCE-FILE instead"
    "Function ASDF:SYSTEM-DEFINITION-PATHNAME is obsolete.
It used to expose ASDF internals with subtle differences with respect to
user expectations, that have been refactored away since.
We recommend you use ASDF:SYSTEM-SOURCE-FILE instead
for a mostly compatible replacement that we're supporting,
or even ASDF:SYSTEM-SOURCE-DIRECTORY or ASDF:SYSTEM-RELATIVE-PATHNAME
    "DEPRECATED. This function used to expose ASDF internals with subtle
differences with respect to user expectations, that have been refactored
away since. We recommend you use ASDF:SYSTEM-SOURCE-FILE instead for a
mostly compatible replacement that we're supporting, or even
ASDF:SYSTEM-SOURCE-DIRECTORY or ASDF:SYSTEM-RELATIVE-PATHNAME
if that's whay you mean." ;;)
    (system-source-file x))


  ;; TRAVERSE is the function used to compute a plan in ASDF 1 and 2.
  ;; It was never officially exposed but some people still used it.
  (defgeneric* (traverse) (operation component &key &allow-other-keys)
    (:documentation
     "Generate and return a plan for performing OPERATION on COMPONENT.
@@ -79,6 +106,7 @@ processed in order by OPERATE."))

;;;; ASDF-Binary-Locations compatibility
;; This remains supported for legacy user, but not recommended for new users.
;; We suspect there are no more legacy users in 2016.
(with-upgradability ()
  (defun enable-asdf-binary-locations-compatibility
      (&key
+41 −15
Original line number Diff line number Diff line
@@ -23,22 +23,28 @@

(with-upgradability ()
  (defclass bundle-op (basic-compile-op)
    ;; NB: use of instance-allocated slots for operations is DEPRECATED
    ;; and only supported in a temporary fashion for backward compatibility.
    ;; Supported replacement: Define slots on program-system instead.
    ((build-args :initarg :args :initform nil :accessor extra-build-args)
     (name-suffix :initarg :name-suffix :initform nil)
     (bundle-type :initform :no-output-file :reader bundle-type)
     #+(or clasp ecl) (lisp-files :initform nil :accessor extra-object-files)))
     #+(or clasp ecl) (lisp-files :initform nil :accessor extra-object-files))
    (:documentation "base class for operations that bundle outputs from multiple components"))

  (defclass monolithic-op (operation) ()
    (:documentation "A MONOLITHIC operation operates on a system *and all of its
dependencies*.  So, for example, a monolithic concatenate operation will
concatenate together a system's components and all of its dependencies, but a
simple concatenate operation will concatenate only the components of the system
itself.")) ;; operation on a system and its dependencies
itself."))

  (defclass monolithic-bundle-op (monolithic-op bundle-op)
    ;; Old style way of specifying prologue and epilogue on ECL: in the monolithic operation
    ;; Old style way of specifying prologue and epilogue on ECL: in the monolithic operation.
    ;; DEPRECATED. Supported replacement: Define slots on program-system instead.
    ((prologue-code :initform nil :accessor prologue-code)
     (epilogue-code :initform nil :accessor epilogue-code)))
     (epilogue-code :initform nil :accessor epilogue-code))
    (:documentation "operations that are both monolithic-op and bundle-op"))

  (defclass program-system (system)
    ;; New style (ASDF3.1) way of specifying prologue and epilogue on ECL: in the system
@@ -66,6 +72,7 @@ itself.")) ;; operation on a system and its dependencies
    (:documentation "Abstract operation for linking files together"))

  (defclass gather-op (bundle-op)
    ;; TODO: rename the slot and reader gather-op to gather-operation
    ((gather-op :initform nil :allocation :class :reader gather-op)
     (gather-type :initform :no-output-file :allocation :class :reader gather-type))
    (:documentation "Abstract operation for gathering many input files from a system"))
@@ -73,6 +80,12 @@ itself.")) ;; operation on a system and its dependencies
  (defun operation-monolithic-p (op)
    (typep op 'monolithic-op))

  ;; Dependencies of a gather-op are the actions of the dependent operation
  ;; for all the (sorted) required components for loading the system.
  ;; Monolithic operations typically use lib-op as the dependent operation,
  ;; and all system-level dependencies as required components.
  ;; Non-monolithic operations typically use compile-op as the dependent operation,
  ;; and all transitive sub-components as required components (excluding other systems).
  (defmethod component-depends-on ((o gather-op) (s system))
    (let* ((mono (operation-monolithic-p o))
           (deps
@@ -85,16 +98,19 @@ itself.")) ;; operation on a system and its dependencies
      `((,(make-operation (or (gather-op o) (if mono 'lib-op 'compile-op))) ,@deps)
        ,@(call-next-method))))

  ;; create a single fasl for the entire library
  ;; Create a single fasl for the entire library
  (defclass basic-compile-bundle-op (bundle-op)
    ((gather-type :initform #-(or clasp ecl mkcl) :fasl #+(or clasp ecl mkcl) :object
                  :allocation :class)
     (bundle-type :initform :fasl :allocation :class)))
     (bundle-type :initform :fasl :allocation :class))
    (:documentation "Base class for compiling into a bundle"))

  ;; Analog to prepare-op, for load-bundle-op and compile-bundle-op
  (defclass prepare-bundle-op (sideway-operation)
    ((sideway-operation
      :initform #+(or clasp ecl mkcl) 'load-bundle-op #-(or clasp ecl mkcl) 'load-op
      :allocation :class)))
      :allocation :class))
    (:documentation "Operation class for loading the bundles of a system's dependencies"))

  (defclass lib-op (link-op gather-op non-propagating-operation)
    ((gather-type :initform :object :allocation :class)
@@ -104,7 +120,7 @@ for all the linkable object files associated with the system. Compare with DLL-O

On most implementations, these object files only include extensions to the runtime
written in C or another language with a compiler producing linkable object files.
On CLASP, ECL, MKCL, these object files also include the contents of Lisp files
On CLASP, ECL, MKCL, these object files _also_ include the contents of Lisp files
themselves. In any case, this operation will produce what you need to further build
a static runtime for your system, or a dynamic library to load in an existing runtime."))

@@ -186,6 +202,7 @@ for all the linkable object files associated with the system or its dependencies
    ((bundle-type :initform :program))
    (:documentation "create an executable file from the system and its dependencies"))

  ;; From the ASDF-internal bundle-type identifier, get a filesystem-usable pathname type.
  (defun bundle-pathname-type (bundle-type)
    (etypecase bundle-type
      ((or null string) ;; pass through nil or string literal
@@ -212,6 +229,7 @@ for all the linkable object files associated with the system or its dependencies
      ((eql :program) ;; the type of an executable program
       (os-cond ((os-unix-p) nil) ((os-windows-p) "exe")))))

  ;; Compute the output-files for a given bundle action
  (defun bundle-output-files (o c)
    (let ((bundle-type (bundle-type o)))
      (unless (or (eq bundle-type :no-output-file) ;; NIL already means something regarding type.
@@ -236,14 +254,19 @@ for all the linkable object files associated with the system or its dependencies
      (setf *image-entry-point* (ensure-function (component-entry-point c)))))

  (defclass compiled-file (file-component)
    ((type :initform #-(or clasp ecl mkcl) (compile-file-type) #+(or clasp ecl mkcl) "fasb")))
    ((type :initform #-(or clasp ecl mkcl) (compile-file-type) #+(or clasp ecl mkcl) "fasb"))
    (:documentation "Class for a file that is already compiled,
e.g. as part of the implementation, of an outer build system that calls into ASDF,
or of opaque libraries shipped along the source code."))

  (defclass precompiled-system (system)
    ((build-pathname :initarg :fasl)))
    ((build-pathname :initarg :fasl))
    (:documentation "Class For a system that is delivered as a precompiled fasl"))

  (defclass prebuilt-system (system)
    ((build-pathname :initarg :static-library :initarg :lib
                     :accessor prebuilt-system-static-library))))
                     :accessor prebuilt-system-static-library))
    (:documentation "Class for a system delivered with a linkable static library (.a/.lib)")))


;;;
@@ -259,6 +282,7 @@ for all the linkable object files associated with the system or its dependencies
                                         &key (name-suffix nil name-suffix-p)
                                         &allow-other-keys)
    (declare (ignore initargs name-suffix))
    ;; TODO: make that class slots or methods, not instance slots
    (unless name-suffix-p
      (setf (slot-value instance 'name-suffix)
            (unless (typep instance 'program-op)
@@ -298,8 +322,8 @@ for all the linkable object files associated with the system or its dependencies
;;;
(with-upgradability ()
  (defun direct-dependency-files (o c &key (test 'identity) (key 'output-files) &allow-other-keys)
    ;; This file selects output files from direct dependencies;
    ;; your component-depends-on method better gathered the correct dependencies in the correct order.
    ;; This function selects output files from direct dependencies;
    ;; your component-depends-on method must gather the correct dependencies in the correct order.
    (while-collecting (collect)
      (map-direct-dependencies
       t o c #'(lambda (sub-o sub-c)
@@ -315,6 +339,7 @@ for all the linkable object files associated with the system or its dependencies
       o c :key 'output-files
           :test (pathname-type-equal-function (bundle-pathname-type (gather-type o))))))

  ;; Find the operation that produces a given bundle-type
  (defun select-bundle-operation (type &optional monolithic)
    (ecase type
      ((:dll :shared-library)
@@ -332,6 +357,7 @@ for all the linkable object files associated with the system or its dependencies
  ;; It must die, and so must any use of initargs in operation,
  ;; unless keys to the asdf-cache are substantially modified to accommodate for them.
  ;; Coordinate with the ECL maintainers to get them to stop using it.
  ;; SUPPORTED REPLACEMENT: Use program-op and program-system
  (defun make-build (system &rest args &key (monolithic nil) (type :fasl)
                             (move-here nil move-here-p)
                             &allow-other-keys)
@@ -538,7 +564,7 @@ for all the linkable object files associated with the system or its dependencies
        #+mkcl (make-pathname :name name :type (bundle-pathname-type :lib) :defaults #p"sys:")
        #+mkcl (make-pathname :name name :type (bundle-pathname-type :lib) :defaults #p"sys:contrib;")))))

  (defun make-library-system (name &optional (pathname (system-module-pathname name)))
  (defun make-prebuilt-system (name &optional (pathname (system-module-pathname name)))
    "Creates a prebuilt-system if PATHNAME isn't NIL."
    (when pathname
      (make-instance 'prebuilt-system
@@ -554,7 +580,7 @@ for all the linkable object files associated with the system or its dependencies
                         (and (system-source-directory x)
                              (list s)))
                       (if-let (p (system-module-pathname x))
                         (list (make-library-system x p)))))))
                         (list (make-prebuilt-system x p)))))))
        `((,lib-op
           ,@(unless (no-uiop c)
               (append (ensure-linkable-system "cmp")
+33 −9

File changed.

Preview size limit exceeded, changes collapsed.

Loading