On this page:
2.1 Package Definition Terms
define
description
edition
input
metadatum
name
os-support
output
provider
racket-versions
revision-names
revision-number
tags
url
2.2 Reprovided Binding Index
2.3 Static Operations for Package Definitions
PACKAGE_  DEFINITION_  MODULE_  LANG
PACKAGE_  DEFINITION_  READER_  LANG
2.3.1 Package Definition Construction and Destructuring
make-package-definition-datum
get-package-definition-body
bare-pkgdef?
package-definition-datum?
2.3.2 Package Definition Analysis
get-static-abbreviated-query
get-static-simple-string
get-static-inputs
get-static-simple-value
analyze-input-expression
2.3.3 Package Definition Code Generation
make-input-expression
autocomplete-input-expression
autocomplete-inputs
2.3.4 Package Definition Transformations
override-inputs
8.0

2 Package Definitions

 (require xiden/pkgdef) package: xiden

xiden/pkgdef is a functional module language for writing package definitions. It expands using a collection pass for the terms defined in Package Definition Terms. Those terms apply to an instance of package, starting from empty-package. The result is provided from the module as pkg.

2.1 Package Definition Terms

syntax

(define id value)

(define (id formals ...) body ...)
define is allowed in module context to bind procedures and common values. All defines are hoisted above all other terms before evaluation.

syntax

(description string-fragment ...)

 
  string-fragment : non-empty-string?

This form will gather all string fragments into a single string, so that you can divide up the message in source code.

Note that the string fragments are concatenated as-is, so take care with whitespace placement.

(description "This is a "
             "string that will"
             " appear as one line.")

syntax

(edition str)

 
  str : non-empty-string?

syntax

(input name)

(input name integrity)
(input name integrity signature)
Adds a package input to package-inputs. A form corresponds exactly to an application of make-input-info.

syntax

(metadatum id value)

Adds a string with 'id to package-metadata.

syntax

(name str)

 
  str : non-empty-string?

syntax

(os-support os ...)

Each os must be a possible value of (system-type 'os).

syntax

(output name body ...)

 
  name : non-empty-string?
Defines a package output, which is a named subprogram where body sits in an implicit mdo form. The output is encoded as part of the implementation of package-build. The name is added to package-output-names.

syntax

(provider str)

 
  str : non-empty-string?

syntax

(racket-versions supported ...)

 
supported = (min-version max-version)
  | exact-version

Each supported subform may be an inclusive Racket version range or an exact Racket version, e.g. (racket-versions ("6.0" "7.7.0.5") "5.4").

You may replace any version string with "*" to remove a bound. This way, (racket-versions ("6.0" "*")) represents all versions above and including 6.0. If the version string is not "*", it must be a valid-version?.

syntax

(revision-names str ...)

 
  str : non-empty-string?

syntax

(revision-number num)

 
  num : revision-number?

syntax

(tags t ...)

 
  t : non-empty-string?

syntax

(url location)

 
  location : url-string?

2.2 Reprovided Binding Index

xiden/pkgdef reprovides bindings from several other modules in the collection. They are indexed here for reference.

2.3 Static Operations for Package Definitions

 (require xiden/pkgdef/static) package: xiden

A package definition is a PACKAGE_DEFINITION_MODULE_LANG module as a syntax object or a list. When a package definition is a list, it matches package-definition-datum?. Each package definition is used as a Racket module that combines discovery information with build instructions for packages.

Collection paths for a module language and reader extension used to write package definitions.

2.3.1 Package Definition Construction and Destructuring

procedure

(make-package-definition-datum [#:id id] 
  body) 
  package-definition-datum?
  id : symbol? = 'pkgdef
  body : list?

procedure

(get-package-definition-body datum)  list?

  datum : package-definition-datum?
Returns the top-level forms of the module code in datum.

A contract that matches a bare package definition.

2.3.2 Package Definition Analysis

The following procedures return data found from matching against the code of a bare package definition. They do not run package definition code.

procedure

(get-static-abbreviated-query pkgdef)  package-query?

  pkgdef : bare-pkgdef?
Returns a package query containing the provider, package, edition, and revision number in pkgdef.

procedure

(get-static-simple-string pkgdef id)  any/c

  pkgdef : bare-pkgdef?
  id : symbol?
Equivalent to (get-static-simple-value stripped id "default").

The return value is assumed to be a string, but might not be.

procedure

(get-static-inputs pkgdef)  list?

  pkgdef : bare-pkgdef?
Returns a list of all input expressions in pkgdef.

procedure

(get-static-simple-value pkgdef id default)  any/c

  pkgdef : bare-pkgdef?
  id : symbol?
  default : any/c
Searches the top-level code of pkgdef for a S-expression of form (id val). Returns the datum in val’s position, or default if no such expression exists.

procedure

(analyze-input-expression expr continue)  any

  expr : any/c
  continue : 
(-> (or/c #f non-empty-string?)
    (or/c #f (listof non-empty-string?))
    (or/c #f md-algorithm/c)
    (or/c #f (or/c bytes?
                   (list/c (or/c 'hex 'base32 'base64)
                           (or/c bytes? string?))))
    (or/c #f string?)
    (or/c #f (or/c bytes?
                   (list/c (or/c 'hex 'base32 'base64)
                           (or/c bytes? string?))))
    any)
Inspects expr to see if it has the shape of an S-expression for a package input, e.g. '(input "name" (sources "a" "b") (integrity 'sha384 #"...")).

If expr does not resemble an input expression, then analyze-input-expression returns expr. Otherwise, analyze-input-expression returns continue applied to information extracted from expr.

In this context, "resemble" means that expr may omit information but not use incorrect information. So, '(input) resembles an input expression, but is missing data. On the other hand, (input (signature ...) 8 (sources ...)) is said to not resemble an input expression because the available data appears incorrect, or irrelevant. Data is not validated beyond basic matching; subexpressions are not evaluated.

Under this definition, continue is applied with the following arguments.

2.3.3 Package Definition Code Generation

procedure

(make-input-expression 
  path-or-port 
  [#:local-name local-name 
  #:byte-encoding byte-encoding 
  #:md-algorithm message-digest-algorithm] 
  make-sources 
  public-key-source 
  private-key-path 
  [private-key-password-path]) 
  list?
  path-or-port : (or/c path-string? input-port?)
  local-name : string? = '(see definition)
  byte-encoding : (or/c #f xiden-encoding/c) = 'base64
  message-digest-algorithm : md-algorithm/c = 'sha384
  make-sources : (-> bytes? path-string? (non-empty-listof any/c))
  public-key-source : string?
  private-key-path : path-string?
  private-key-password-path : (or/c #f path-string?) = #f
Returns an input expression (as a list datum) from several pieces of information stored in files. All given files must exist. Do not use this procedure with untrusted data.

If local-name is not set and path-or-port is a path-string?, then local-name is set to (path->string (file-name-from-path path-or-port)).

For example, consider the following application:

(make-input-expression
  "source-code.tar.gz"
  #:local-name "code.tar.gz"
  #:byte-encoding 'base64
  (lambda (digest path) (list "https://example.com/code.tar.gz"))
  'sha384
  "https://example.com/public-key.pem"
  "~/path/to/private-key.pem"
  "~/path/to/private-key-password.txt")

Xiden uses OpenSSL subprocesses to sign digests. To prevent *nix monitoring tools like top from seeing your private key’s password, it sends the password to OpenSSL using a file path. This is why you cannot pass your password directly, but be sure to securely delete the password file the moment you are done using it.

This takes an archive called "source-code.tar.gz" from the file system. In the package definition, it will be referred to simply as "code.tar.gz". The source list is computed dynamically from make-sources, which accepts the message digest (as bytes) and a reference eq? to path as arguments. Note that the sources are used literally in the output expression, so procedures like (lambda (digest path) `((from-file ,(bytes->string/utf-8 digest)))) are valid.

The input expression will contain integrity information using a 'sha384 digest. Finally, the signature information will contain the exact public key source and a signature computed from the private key and, optionally, the password used to access the private key.

The output expression will look something like this:

'(input "code.tar.gz"
        (sources "https://example.com/code.tar.gz")
        (integrity 'sha384 (base64 "..."))
        (signature "https://example.com/public-key.pem"
                   (base64 "...")))

All expressions of byte strings are encoded using byte-encoding. If byte-encoding is #f, then byte strings are embedded directly in the output expression.

This procedure can be used with a REPL to help copy and paste input expressions into a package definition. A more convenient way to realize this is to hold every argument except the input path constant. This allows authors to define several inputs the same way.

(define (mkinput . paths)
  (for/list ([p (in-list paths)])
    (make-input-expression
      p
      (lambda (digest also-p) (list (format "https://example.com/~a" (file-name-from-path also-p))))
      'sha384
      "https://example.com/public-key.pem"
      "~/path/to/private-key.pem"
      "~/path/to/private-key-password.txt")))

procedure

(autocomplete-input-expression 
  expr 
  [#:default-name default-name] 
  #:public-key-source public-key-source 
  #:find-data find-data 
  #:private-key-path private-key-path 
  [#:byte-encoding byte-encoding 
  #:default-md-algorithm default-md-algorithm 
  #:override-sources override-sources 
  #:private-key-password-path private-key-password-path]) 
  any/c
  expr : any/c
  default-name : non-empty-string? = DEFAULT_STRING
  public-key-source : non-empty-string?
  find-data : procedure?
  private-key-path : path-string?
  byte-encoding : (or/c #f xiden-encoding/c) = 'base64
  default-md-algorithm : md-algorithm/c = 'sha384
  override-sources : procedure? = (λ (d p s) s)
  private-key-password-path : (or/c path-string? #f) = #f
Programatically finishes incomplete input expressions.

Equivalent to

(analyze-input-expression expr
                          public-key-source
                          md-algorithm
                          (λ (n s md ib pk sb)
                            (make-input-expression
                             (find-data n s md ib pk sb)
                             #:local-name (or n default-name)
                             #:byte-encoding byte-encoding
                             #:md-algorithm (or md default-md-algorithm)
                             (λ (d p) (override-sources d p s))
                             public-key-source
                             private-key-path
                             private-key-password-path)))

procedure

(autocomplete-inputs 
  stripped 
  [#:default-name default-name] 
  #:public-key-source public-key-source 
  #:find-data find-data 
  #:private-key-path private-key-path 
  [#:byte-encoding byte-encoding 
  #:default-md-algorithm default-md-algorithm 
  #:override-sources override-sources 
  #:private-key-password-path private-key-password-path]) 
  bare-racket-module?
  stripped : bare-racket-module?
  default-name : non-empty-string? = DEFAULT_STRING
  public-key-source : non-empty-string?
  find-data : procedure?
  private-key-path : path-string?
  byte-encoding : (or/c #f xiden-encoding/c) = 'base64
  default-md-algorithm : md-algorithm/c = 'sha384
  override-sources : procedure? = (λ (d p s) s)
  private-key-password-path : (or/c path-string? #f) = #f
Applies autocomplete-input-expression to each element of the code in stripped, returning a new bare module. All inputs will be signed using the same private key, all integrity information will be generated in the same way, and all byte expressions will be encoded the same way.

2.3.4 Package Definition Transformations

procedure

(override-inputs pkgdef input-exprs)  bare-pkgdef?

  pkgdef : bare-pkgdef?
  input-exprs : list?
Functionally replaces any package inputs in pkgdef that share a name with at least one element in input-exprs.

If multiple expressions in input-exprs share a name, only the last occurrance will be used in the output.