Home

Awesome

<img src="doc/bootleg-logo.svg" width="100px">

bootleg

Github Action CircleCI

Static website generation made simple. A powerful, fast, clojure html templating solution.

Bootleg is a command line tool that rapidly renders clojure based templates. With inbuilt support for html, hiccup, hickory, selmer, mustache, markdown, enlive, json, yaml and edn, it enables you to pull together disparate elements and glue them together to generate the static website of your dreams.

Quickstart

Install for Linux:

$ curl -LO https://github.com/retrogradeorbit/bootleg/releases/download/v0.1.9/bootleg-0.1.9-linux-amd64.tgz
$ tar xvf bootleg-0.1.9-linux-amd64.tgz
$ mv bootleg ~/bin

Install for MacOS:

$ curl -LO https://github.com/retrogradeorbit/bootleg/releases/download/v0.1.9/bootleg-0.1.9-macos-amd64.zip
$ unzip bootleg-0.1.9-macos-amd64.zip
$ mv bootleg /usr/local/bin

Clone this repository and change into the examples/quickstart directory:

$ git clone https://github.com/retrogradeorbit/bootleg.git
$ cd bootleg/examples/quickstart

A simple page:

$ cat example-simple.clj
[:html
 [:body
  [:h1 "A simple webpage"]
  [:p "Made with bootleg for maximum powers!"]]]
$ bootleg example-simple.clj
<html><body><h1>A simple webpage</h1><p>Made with bootleg for maximum powers!</p></body></html>

A dynamic example:

$ cat example-dynamic.clj
[:div.countdown
 (for [n (range 10 0 -1)]
   [:p n])
 [:p "blast off!"]]
$ bootleg example-dynamic.clj
<div class="countdown"><p>10</p><p>9</p><p>8</p><p>7</p><p>6</p><p>5</p><p>4</p><p>3</p><p>2</p><p>1</p><p>blast off!</p></div>

Mustache:

$ cat example-mustache.clj
(mustache "quickstart.html" (yaml "fields.yml"))
$ cat quickstart.html
<h1>{{ title }}</h1>
<h2>by {{ author }}</h2>
<div>{{& body }}</div>
$ cat fields.yml
title: Bootleg
author: Crispin
body: I'm going to rewrite all my sites with this!
$ bootleg example-mustache.clj
<h1>Bootleg</h1>
<h2>by Crispin</h2>
<div>I'm going to rewrite all my sites with this!</div>

Markdown support. Evaluate from command line. Easy downloading of resources by url (for any command):

$ bootleg -e '(markdown "https://raw.githubusercontent.com/retrogradeorbit/bootleg/master/README.md")'
<h1>bootleg</h1><p>Static website generation made simple. A powerful, fast, clojure templating solution that rocks!...

CSS selector based processing. The magic of enlive:

$ cat example-enlive.clj
(-> (markdown "simple.md")
    (enlive/at [:p] (enlive/set-attr :style "color:green;")))
$ cat simple.md
# Markdown support

This is some simple markdown
$ bootleg example-enlive.clj
<h1>Markdown support</h1><p style="color:green;">This is some simple markdown</p>

Combine hiccup, mustache, markdown and enlive processing:

$ cat example-combine.clj
(mustache "quickstart.html"
          (assoc (yaml "fields.yml")
                 :body (markdown "simple.md" :html)))
$ bootleg example-combine.clj
<h1>Bootleg</h1>
<h2>by Crispin</h2>
<div><h1>Markdown support</h1><p>This is some simple markdown</p></div>

Data output with -d flag:

$ cat example-data.clj
(-> (mustache "quickstart.html"
              (assoc (yaml "fields.yml")
                     :body (markdown "simple.md" :html)))
    (convert-to :hickory-seq))
$ bootleg -d example-data.clj
({:type :element, :attrs nil, :tag :h1, :content ["Bootleg"]}
 "\n"
 {:type :element, :attrs nil, :tag :h2, :content ["by Crispin"]}
 "\n"
 {:type :element,
  :attrs nil,
  :tag :div,
  :content [{:type :element,
             :attrs nil,
             :tag :h1,
             :content ["Markdown support"]}
            {:type :element,
             :attrs nil,
             :tag :p,
             :content ["This is some simple markdown"]}]}
 "\n")
$ bootleg example-data.clj          # <- no -d flag means output will be html
<h1>Bootleg</h1>
<h2>by Crispin</h2>
<div><h1>Markdown support</h1><p>This is some simple markdown</p></div>

Installation

Bootleg is distributed for linux as a single executable file. Download the latest tarball from https://github.com/retrogradeorbit/bootleg/releases and then extract it. Once extracted, move the binary to your path. For system wide installation try /usr/local/bin or for personal use ~/bin

$ curl -LO https://github.com/retrogradeorbit/bootleg/releases/download/v0.1.9/bootleg-0.1.9-linux-amd64.tgz
$ tar xvf bootleg-0.1.9-linux-amd64.tgz
$ mv bootleg /usr/local/bin

Other Platforms

Windows support is experimental. Download (https://github.com/retrogradeorbit/bootleg/releases/download/v0.1.9/bootleg-0.1.9-windows-amd64.zip) and unzip the archive. Copy the bootleg.exe binary to somewhere on your path.

The jar release file is also an option if you have java installed. You can run it as follows:

$ java -jar bootleg-0.1.9.jar

Usage

Run at the command line for options:

$ bootleg -h
Static website generation made simple. A powerful, fast, clojure html templating solution.

Usage: bootleg [options] [clj-file]

Options:
  -h, --help           Print the command line help
  -v, --version        Print the version string and exit
  -e, --evaluate CODE  Pass in the hiccup to evaluate on the command line
  -d, --data           Output the rendered template as a clojure form instead of html
  -o, --output FILE    Write the output to the specified file instead of stdout
  -t, --traceback      Print the full exception traceback
  -c, --colour         Print outputs in colour where appropriate
      --color          Alias for --colour

Babashka Pod Usage

Note: There is a known issue with this pod and bb v0.2.1. Use bb v0.2.2 or higher.

Install the binary, then launch the pod in babashka by invoking the binary:

(ns mybbscript
  (:require [babashka.pods :as pods]))

(pods/load-pod "bootleg")
(require '[pod.retrogradeorbit.bootleg.utils :as utils])

(-> [:div
      [:h1 "Using Bootleg From Babashka"]
      [:p "This is a demo"]]
  (utils/convert-to :html))
;;=> "<div><h1>Using Bootleg From Babashka</h1><p>This is a demo</p></div>"

When invoked as a pod, bootleg reveals the following namespaces to your babashka script:

Useful namespaces.

Compatibility

Bootleg 0.1.9 requires babashka 0.0.98 or higher. Bootleg 0.1.9 requires babashka 0.0.99 or higher.

As a large library with lots of namespaces and functionality there may be parts that dont work correctly. Please open tickets for any such issues mentioning the problem is for the pod.

An example of testing verified working parts can be found here: https://github.com/retrogradeorbit/bootleg/blob/master/test/pod/bbpodtest.clj

Overview

bootleg loads and evaluates the clj file specified on the command line, or evaluates the CODE form specified in the -e flag. The code can return any of the supported data structures listed below. bootleg will then automatically convert that format into html and write it out to standard out, or to FILE if specified. This conversion to html step can be prevented by calling with the -d flag. In this case the output will be a pretty formatted edn form of the output data structure.

bootleg supports five main markup data structures. Three are more flexible. And two are limited. We will begin describing the two limited data structures and why they are limited.

hiccup

Hiccup is a standard clojure DSL syntax for representing markup as nested sequences of vectors, and is represented in option flags by the keyword :hiccup. An example of some hiccup: the html <div><p>This is an example</p></div> is represented in hiccup as [:div [:p "This is an example"]].

Hiccup, as the term is used in bootleg, is refering to this vector form. It represents a single root element and its children. This means there are template fragments that cannot be represented like this. For example, the html snippet <p>one</p><p>two</p> cannot be represented as hiccup. It is comprised of two hiccup forms. [:p "one"] and [:p "two"]. See hiccup-seq below for information about this form.

hickory

Hickory is a format used to internally represent document trees in clojure for programmatic processing. In option flags it is referenced by the keyword :hickory. It is very verbose and not suitable to write by hand. It is supported internally for passing between functions that use it. A simple example of some hickory: the html <p>one</p> is represented in hickory as {:type :element, :attrs nil, :tag :p, :content ["one"]}.

Both the hickory and enlive clojure projects use this format internally to represent and manipulate DOM trees.

Hickory, as the term is used in bootleg, is refering to this hashmap form. It represents a single root element and its children. This means there are template fragments that cannot be represented in hickory. For example, the html snippet <p>one</p><p>two</p> cannot be represented as hickory. It is comprised of two hickory forms. {:type :element, :attrs nil, :tag :p, :content ["one"]} and {:type :element, :attrs nil, :tag :p, :content ["two"]}. See hickory-seq below for information about this form.

hiccup-seq

Hiccup-seq is simply a clojure sequence (or vector) of hiccup forms. In option flags it is referenced by the keyword :hiccup-seq. By wrapping multiple hiccup forms in a sequence, hiccup-seq can represent any single root element (and it's children) and any template fragment composed of sibling elements.

For example: the html snippet <p>one</p><p>two</p> is represented in hiccup-seq as: ([:p "one"] [:p "two"])

hickory-seq

Hickory-seq is simply a clojure sequence (or vector) of hickory forms. In option flags it is referenced by the keyword :hickory-seq. By wrapping multiple hickory forms in a sequence, hickory-seq can represent any single root element (and it's children) and any template fragment composed of sibling elements.

For example: the html snippet <p>one</p><p>two</p> is represented in hickory-seq as: ({:type :element, :attrs nil, :tag :p, :content ["one"]} {:type :element, :attrs nil, :tag :p, :content ["two"]})

html

html is represented internally as a string. This is a flexible type and can hold a root element and children, or a number of sibling elements sequentially.

xml

xml is represented internally as a string. This type always starts with an XML header <?xml version="..."?>

You can output xml with bootleg by converting to type :xml first:

$ bootleg -e '(convert-to [:link "foo"] :xml)'
<?xml version="1.0" encoding="UTF-8"?><link>foo</link>

Inbuilt functions

The following functions are inbuilt into the clojure interpreter:

Markup Processing Functions

markdown

(markdown source & options)

Load the markdown from the file specified in source and render it. source can be a local file path (relative to the executing hiccup file location) or a URL to gather the markdown from.

Options can be used to alter the behaviour of the function. Options are a list of keywords and can be specified in any order after the source parameter. Options can be:

eg.

$ bootleg -e '(markdown "# heading\nparagraph" :data)'
<h1>heading</h1><p>paragraph</p>
$ bootleg -d -e '(markdown "# heading\nparagraph" :data :hickory-seq)'
({:type :element, :attrs nil, :tag :h1, :content ["heading"]}
 {:type :element, :attrs nil, :tag :p, :content ["paragraph"]})
$ bootleg -d -e '(markdown "# heading\nparagraph" :data :hiccup-seq)'
([:h1 "heading"] [:p "paragraph"])
$ bootleg -d -e '(markdown "# heading\nparagraph" :data :html)'
"<h1>heading</h1><p>paragraph</p>"

mustache

(mustache source vars & options)

Load a mustache template from the file specified in source and render it substituting the vars from vars. source can be a local file path (relative to the executing hiccup file location) or a URL to gather the markdown from.

Options can be used to alter the behaviour of the function. Options are a list of keywords and can be specified in any order after the source parameter. Options can be:

eg.

$ bootleg -e '(mustache "<p>{{var1}}</p><div>{{&var2}}</div>" {:var1 "value 1" :var2 "<p>markup</p>"} :data)'
<p>value 1</p><div><p>markup</p></div>
$ bootleg -d -e '(mustache "<p>{{var1}}</p>" {:var1 "value 1"} :data :hiccup-seq)'
([:p "value 1"])

selmer

(selmer source vars & options)

Load a selmer template from the file specified in source and render it substituting the vars from vars. source can be a local file path (relative to the executing hiccup file location) or a URL to gather the markdown from.

Options can be used to alter the behaviour of the function. Options are a list of keywords and can be specified in any order after the source parameter. Options can be:

eg.

$ bootleg -e '(selmer "<p>Hello {{name|capitalize}}!</p>" {:name "world"} :data)'
<p>Hello World!</p>
$ bootleg -d -e '(selmer "<p>Hello {{name|capitalize}}!</p>" {:name "world"} :data :hiccup-seq)'
([:p "Hello World!"])

The selmer namespaces are also provided at their usual namespace locations.

slurp

(slurp source)

Load the contents of a file, from a local or remote source, into memory and return it. This slurp can load from URLs. Does no interpretation of the file contents at all. Returns them as is.

html

(html source & options)

Loads the contents of a html or xml file and returns them in :hiccup-seq (by default).

Options can be:

eg.

$ bootleg -d -e '(html "<h1>heading</h1><p>body</p>" :data :hiccup-seq)'
([:h1 "heading"] [:p "body"])
$ bootleg -d -e '(html "<div><h1>heading</h1><p>body</p></div>" :data :hiccup)'
[:div [:h1 "heading"] [:p "body"]]

hiccup

(hiccup source)

Loads and evaluates the clojure source from another file.

Filesystem Functions

glob

(glob pattern)

Returns a sequence of files that match the globbing pattern pattern. Supports *, **, ?, [abc], [a-z], [!a] and relative file paths . and ... File paths are returned relative to the directory of the executing file.

$ bootleg -d -e '(glob "**/*.y?l")'
(".github/workflows/deploy.yml"
 ".circleci/config.yml"
 "examples/quickstart/fields.yml"
 "test/files/simple.yml")

symlink

(symlink link target)

Make a symlink from link to target. Operates idempotently. If the link already exists it does nothing. If the link exists but points to another target, changes the link to point to the specified target. If the link exists but is not a symbolic link, throws an exception. On success returns the path of the link.

mkdir

(mkdir path)

Make a directory path. Does not create any parent directories. Operates idempotently. If the direcotry exists, it does nothing. Otherwise it tries to create the directory. On success it returns the directory path.

mkdirs

(mkdirs path)

Make a directory path including all the parent directories. Operates idempotently. If the direcotry exists, it does nothing. Otherwise it tries to create the directories. On success it returns the final directory path.

spit

(spit filename data)

Write data into the specified filename. The filename is interpereted relative to the path of the current script.

Var Loading Functions

yaml

(yaml source & options)

Load yaml data from source and process it. Returns the parsed data structure.

Options can be:

json

(json source & options)

Load json data from source and process it. Returns the parsed data structure.

Options can be:

edn

(edn source & options)

Load edn data from source and process it. Returns the parsed data structure.

Options can be:

Data Testing

is-hiccup?

(is-hiccup? data)

Test the markup in data. Return true if the data is a hiccup form.

is-hiccup-seq?

(is-hiccup-seq? data)

Test the markup in data. Return true if the data is a sequence of hiccup forms.

is-hickory?

(is-hickory? data)

Test the markup in data. Return true if the data is a hickory form.

is-hickory-seq?

(is-hickory-seq? data)

Test the markup in data. Return true if the data is a sequence of hickory forms.

Data Conversion

convert-to

(convert-to data type)

Convert one supported data type to another. Input data may be hiccup, hiccup-seq, hickory, hickory-seq or html.

Type may be :hiccup, :hiccup-seq, :hickory, :hickory-seq or :html.

Example:

$ bootleg -d -e '(convert-to [:p#id.class "one"] :hickory)'
{:type :element,
 :attrs {:class "class", :id "id"},
 :tag :p,
 :content ["one"]}
$ bootleg -d -e '(convert-to "<p class=\"class\" id=\"id\">one</p>" :hiccup)'
[:p {:class "class", :id "id"} "one"]
$ bootleg -d -e '(convert-to "<p>one</p><p>two</p>" :hiccup-seq)'
([:p "one"] [:p "two"])
$ bootleg -d -e '(convert-to {:type :element :tag :p :content ["one"]} :html)'
"<p>one</p>"
$ bootleg -d -e '(convert-to [:link "foo"] :xml)'
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><link>foo</link>"

Note: Some conversions are lossy. Converting from html or any *-seq data type to hickory or hiccup may lose forms. Only the last form will be returned.

$ bootleg -d -e '(convert-to "<p>one</p><p>two</p>" :hiccup)'
Warning: converting markup from :html to :hiccup lost 1 form
[:p "two"]

as-html

(as-html data)

Convert any supported input format passed into data to html output. Same as (convert-to data :html)

Enlive Processing

Enlive html functions are to be found in the enlive namespace. A new version of at is supplied that provides automatic type coercion for the inputs and outputs.

In addition to this the standard enlive namespaces are available in their usual locations:

at

(at data selector transform & more)

Take the markup data and process every element matching selector through the transform transform

deftemplate

(deftemplate name source args & forms)

Defines a template as a function that returns a hiccup-seq.

** Note ** the original deftemplate returned a sequence of strings

defsnippet

(defsnippet name source selector args & forms)

Define a named snippet -- equivalent to (def name (snippet source selector args ...)).

Returns a hickory-seq on nodes.

Enlive Transforms

content

(content & values)

Replaces the content of the element. Values can be any supported formats: hickory, hickory-seq, hiccup, hiccup-seq or html.

** note: ** This is different to the standard enlive content function. The standard function is present as content*. Passing html to content will embed that markup in the output. In contrast, passing html to content* will insert that html in an escaped form.

html-snippet

(html-snippet & values)

Concatenate values as a string and then parse it with tagsoup. html-snippet doesn't insert missing <html> or <body> tags.

html-content

(html-content & values)

Replaces the content of the element. Values are strings containing html code. This is present for backwards compatibility. The new content function can take and embed html code and should be used instead.

wrap

(wrap tag attr)

Wraps selected node into the given tag. eg. (wrap :div) or (wrap :div {:class "foo"})

unwrap

unwrap

Opposite to wrap, returns the content of the selected node.

set-attr

(set-attr & key-value-pairs)

Assocs attributes on the selected element. eg. (set-attr :attr1 "val1" :attr2 "val2")

remove-attr

(remove-attr & attr-names)

Sets given key value pairs as attributes for selected node. eg. (remove-attr :attr1 :attr2)

add-class

(add-class & classes)

Adds class(es) to the selected node. eg. (add-class "foo" "bar")

remove-class

(add-class & classes)

Removes class(es) from the selected node. eg. (remove-class "foo" "bar")

do->

(do-> & fns)

Chains (composes) several transformations. Applies functions from left to right. eg. (do-> transformation1 transformation2)

append

(append & values)

Appends the values to the content of the selected element. eg. (append "xyz" a-node "abc")

Values can be any supported formats: hickory, hickory-seq, hiccup, hiccup-seq or html.

prepend

(prepend & values)

Prepends the values to the content of the selected element. eg. (prepend "xyz" a-node "abc")

Values can be any supported formats: hickory, hickory-seq, hiccup, hiccup-seq or html.

after

(after & values)

Inserts the values after the current selection (node or fragment). eg. (after "xyz" a-node "abc")

Values can be any supported formats: hickory, hickory-seq, hiccup, hiccup-seq or html.

before

(before & values)

Inserts the values before the current selection (node or fragment). eg. (before "xyz" a-node "abc")

Values can be any supported formats: hickory, hickory-seq, hiccup, hiccup-seq or html.

substitute

(substitute & values)

Replaces the current selection (node or fragment). eg. (substitute "xyz" a-node "abc")

Values can be any supported formats: hickory, hickory-seq, hiccup, hiccup-seq or html.

move

(move src-selector dest-selector) (src-selector dest-selector combiner)

Takes all nodes (under the current element) matched by src-selector, removes them and combines them with the elements matched by dest-selector. eg. (move [:.footnote] [:#footnotes] content)

content*

(content* & values)

The original enlive content transformer. Replaces the content of the element. Values can be hickory nodes, collection of hikory nodes, or plain text strings.

append*

(append* & values)

The original enlive append transformer. Appends the values to the content of the selected element. eg. (append "xyz" a-node "abc")

prepend*

(prepend* & values)

The original enlive prepend transformer. Prepends the values to the content of the selected element. eg. (prepend "xyz" a-node "abc")

after*

(after* & values)

The original enlive after transformer. Inserts the values after the current selection (node or fragment). eg. (after "xyz" a-node "abc")

before*

(before* & values)

The original enlive before transformer. Inserts the values before the current selection (node or fragment). eg. (before "xyz" a-node "abc")

substitute*

(substitute* & values)

The original enlive substitute transformer. Replaces the current selection (node or fragment). eg. (substitute "xyz" a-node "abc")

Hickory

The hickory namespaces are provided at their usual namespace locations.

Other Functions

pprint

(pprint form)

Pretty print to stdout the passed in form. If --colour is passed on command line then print with colour highlighting.

parse-string

(parse-string string)

Parse the passed in string into a clojure type. Useful for converting strings to numbers, keywords, vectors or hashmaps. A binding of edamame's parse-string is used for parsing.

Java Packages

The following java classes are included and can be imported and used:

java.time

$ bootleg -d -e '(import [java.time LocalDate]) (LocalDate/now)'
#object[java.time.LocalDate "0x7d108063" "2020-01-28"]

Passing in Context

Environment Variables

You can access environment variables through the System namespace. This namespace has the getenv function:

$ FOO=bar bootleg -d -e '(System/getenv "FOO")'
"bar"

Command Line Arguments

Command line arguments are available in the *command-line-args* var:

$ bootleg -d -e '*command-line-args*'
("-d" "-e" "*command-line-args*")

Also included is the clojure.tools.cli namespace that you can require as needed to process command line args, or reference its namespace directly:

$ bootleg -d -e '(clojure.tools.cli/parse-opts *command-line-args* [["-e" "--evaluate CODE" ""] ["-d" "--data" ""]])'
{:options {:data true,
           :evaluate "(clojure.tools.cli/parse-opts *command-line-args* [[\"-e\" \"--evaluate CODE\" \"\"] [\"-d\" \"--data\" \"\"]])"},
 :arguments [],
 :summary "  -e, --evaluate CODE\n  -d, --data",
 :errors nil}

If you want to pass in edn in a env var or command line you can use edamame's reader that is already available under parse-string:

$ FOO='{:a 1 :b 2}' bootleg -d -e '(-> "FOO" System/getenv parse-string :b)'
2

Standard Input

When no -e argument is passed on the command line and no filename is supplied then bootleg will read its clojure script from standard in:

$ bootleg <<< '[:p 1]'
<p>1</p>

*in* is bound to clojure.lang.LineNumberingPushbackReader on standard in as it is in clojure. So you can slurp in to process standard in:

echo -n "<html></html>" | bootleg -d -e '(-> *in* slurp (convert-to :hiccup))'
[:html]

Remember to be careful of trailing newlines when using bash's <<< pipe form.

$ bootleg -d -e '(-> *in* slurp (convert-to :hiccup))' <<< '<html></html>'
Warning: converting markup from :html to :hiccup lost 1 form
"\n"
$ bootleg  -d -e '(-> *in* slurp (convert-to :hiccup-seq))' <<< '<html></html>'
([:html] "\n")

Shebang

Bootleg scripts can contain a shebang line. Then if the executable bit is set, they can be run like programmes. Examine the shebang example in examples/quickstart/shebang.clj.

$ cat shebang.clj
#!/usr/bin/env bootleg

[:div
 [:h1 "Command Line Args"]
 [:ul
  (for [arg *command-line-args*]
    [:li arg])]]

Make it executable:

$ chmod a+x shebang.clj

Run it on the command line:

$ ./shebang.clj arg1 arg2 "This is argument 3"
<div><h1>Command Line Args</h1><ul><li>./shebang.clj</li><li>arg1</li><li>arg2</li><li>This is argument 3</li></ul></div>

Building the executable

Ensure graalvm community edition 19.3.0 is installed in your home directory and the native image extension is also installed.

$ make clean
$ make all

The compiled file will be at build/bootleg

To use a different version of graal or one installed in a different path, add the GRAALVM suffix like:

$ make all GRAALVM=/path/to/graal-vm

Examples

The following are some example sites built using bootleg for you to use as inspiration, or as a base for your own work.

Blog Posts

Submit Your Own Example

Have you built a website with bootleg? Is the source of this website open source? If so open an issue on the project with a link to your github project with the source tree in it and I will add it to the examples list. This way there will be more reference material for bootleg users to draw apon!

Thanks

bootleg leverages other people's amazing work. The following projects and people enable this to exist at all.

License

Copyright © 2019 Crispin Wellington

This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0.

This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version, with the GNU Classpath Exception which is available at https://www.gnu.org/software/classpath/license.html.