riju/doc/tutorial/install.md

344 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Tutorial: install your language
Most likely, your language isn't installed by default in Ubuntu. If
not, you'll need to add an `install:` block to your language
configuration describing how to install it.
The easiest case is system packages, which are supported out of the
box, for example:
```yaml
install:
apt:
- ruby
npm:
- pug-cli
pip:
- bython
gem:
- solargraph
cpan:
- Acme::Chef
opam:
- ocamlformat
```
For more advanced configuration you need to understand how Debian
packaging works. To build a package, we place files into a `${pkg}`
directory mirroring how they should be installed on the system, so for
example a binary intended for `/usr/local/bin/prettier` would be
placed at `${pkg}/usr/local/bin/prettier`. Then the `${pkg}` directory
can be turned into a `.deb`, which can be installed on any system.
**If you have trouble, see the tutorial on [debugging package
installation](install-debugging.md).**
## Download a binary or script
We prefer to put all binaries in `/usr/local/bin` rather than
`/usr/bin` where they might conflict with default Ubuntu packages.
```yaml
install:
manual: |
install -d "${pkg}/usr/local/bin"
wget http://www.blue.sky.or.jp/grass/grass.rb
chmod +x grass.rb
cp -T grass.rb "${pkg}/usr/local/bin/grass"
```
## Get the latest version from GitHub
There is a predefined `latest_release` function that can be used to
get the name of the latest tag in a GitHub repository.
```yaml
install:
manual: |
install -d "${pkg}/usr/local/bin"
ver="$(latest_release snoe/clojure-lsp)"
wget "https://github.com/snoe/clojure-lsp/releases/download/${ver}/clojure-lsp"
chmod +x clojure-lsp
cp clojure-lsp "${pkg}/usr/local/bin/"
```
## Get the latest version from some other website
In the case that there's no "download the latest version" URL, we
typically use disgusting `grep` pipelines to extract it in a
semi-reliable way from a stable-seeming webpage.
```yaml
install:
manual: |
install -d "${pkg}/usr/local/bin"
path="$(curl -fsSL https://static.red-lang.org/download.html | grep -Eo '/dl/linux/[^"]+' | head -n1)"
wget "https://static.red-lang.org${path}" -O red
chmod +x red
cp red "${pkg}/usr/local/bin/"
```
## Unpack a tarball
Sometimes tarballs can be extracted directly to `/usr/local`. However,
unless there's an obviously correct way to do that, we typically put
language distributions in `/opt/<lang>` and then put symlinks in
`/usr/local/bin` for the binaries. The `-C` and `--strip-components`
flags are very helpful for controlling the extraction.
Also, one thing that might be unintuitive is that we use system-global
paths for the targets of symlinks (`/opt/swift/bin/swiftc`) but
`${pkg}` paths for the link names (`${pkg}/usr/local/bin/swiftc`).
This is because while we are putting all files into `${pkg}` during
build, the eventual place they will be installed by the package is
into the root filesystem, so any references to paths *within* files
(including symlink targets) must not mention `${pkg}`. This is a
standard feature of all Linux packaging tools.
```yaml
install:
manual: |
install -d "${pkg}/opt/swift"
install -d "${pkg}/usr/local/bin"
ver="$(latest_release apple/swift | grep -Eo '[0-9.]+')"
wget "https://swift.org/builds/swift-${ver}-release/ubuntu2004/swift-${ver}-RELEASE/swift-${ver}-RELEASE-ubuntu20.04.tar.gz" -O swift.tar.gz
tar -xf swift.tar.gz -C "${pkg}/opt/swift" --strip-components=2
ln -s /opt/swift/bin/swiftc /opt/swift/bin/sourcekit-lsp "${pkg}/usr/local/bin/"
```
## Compile from source
Sometimes there is no binary distribution. In this case you want to
clone/download the source code and run the appropriate compilation
command. GCC and LLVM plus a number of scripting languages are already
installed into the `packaging` image by default, but you might have to
include a `prepare` block listing additional things to install to be
able to compile the software.
For example, in this case the `build.sh` script provided by Zot
happens to require some Qt development dependencies at compile time,
and a non-development dependency at runtime.
```yaml
install:
prepare:
apt:
- qt5-qmake
- qtscript5-dev
apt:
- libqt5script5
manual: |
install -d "${pkg}/usr/local/bin"
git clone https://github.com/manyoso/zot.git
pushd zot
./build.sh
cp build/bin/zot "${pkg}/usr/local/bin/"
popd
```
## Use a shared dependency
Your language may need to use software such as Prettier which is also
used by other languages but does not have an Ubuntu package. In that
case we package it as a Riju shared dependency (check the `shared`
directory for a list), and you declare the dependency using the `riju`
key.
```yaml
install:
riju:
- prettier
```
## Install custom scripts or config files
Sometimes you may need to add a wrapper script or config file
somewhere on the filesystem. Riju has the `scripts` and `files` keys
for this. The keys of these maps are the filesystem paths (`scripts`
defaults to putting things in `/usr/local/bin` if relative paths are
given).
```yaml
install:
scripts:
teco-encode: |
#!/usr/bin/env -S python3 -u
import re
import sys
for line in sys.stdin:
line = re.sub(r"\^(.)", lambda m: chr(ord(m.group(1)) ^ 0b1000000), line)
line = line.replace("$", chr(27))
print(line, end="")
files:
"/opt/sqlite/sqls.yml": |
connections:
- driver: sqlite3
dataSourceName: db.sqlite3
```
## Dealing with versioned APT packages
For some reason, some APT packages have version numbers in their
names. For example, you can't `sudo apt install lua`; you have to
`sudo apt install lua5.4`. The best way to deal with these situations
is to use various `grep-aptavail` hacks to identify the latest
available version programmatically. Check the man page as well as uses
of `grep-aptavail` in Riju to understand the options.
```yaml
install:
apt:
- $(grep-aptavail -XF Provides lua -a -XF Version "$(grep-aptavail -XF Provides lua -s Version -n | sort -Vr | head -n1)" -s Package -n | head -n1)
```
## Custom APT repos
Some languages are distributed in third-party APT repositories. You
can identify this by looking for installation directions that say to
run `add-apt-repository` or add a file to `/etc/apt/sources.list.d`.
Frequently you're also asked to import a custom signing key using
`apt-key`.
Riju provides shorthands for these operations. The `aptKey` option can
be either a URL or a hexadecimal string (both forms appear in commands
you're asked to run). The `aptRepo` option is a line that can be added
to `/etc/apt/sources.list.d`, which can be copied out of the suggested
installation command.
Assuming these keys are specified, then all the packages in the custom
APT repo(s) will be available for selection under the `apt` option, in
addition to standard Ubuntu packages.
```yaml
install:
aptKey:
- "https://keybase.io/crystal/pgp_keys.asc"
aptRepo:
- "deb [arch=amd64] https://dist.crystal-lang.org/apt crystal main"
apt:
- crystal
```
## Getting the name of the current Ubuntu release
Sometimes you need to download from a URL or add an APT repository
whose name depends on the version (e.g. `21.04`) or name (e.g.
`hirsute`) of the current Ubuntu release. These parameters are
automatically available in the `${ubuntu_ver}` and `${ubuntu_name}`
variables, which can be used in most option values:
```yaml
install:
aptKey:
- "B4112585D386EB94"
aptRepo:
- "deb [arch=amd64] https://dl.hhvm.com/ubuntu ${ubuntu_name} main"
apt:
- hhvm
```
## Dealing with broken certificate chains
Consider the following errors from `curl` and `wget` respectively:
```
curl: (60) SSL certificate problem: unable to get local issuer certificate
ERROR: cannot verify ioke.org's certificate, issued by CN=R3,O=Let's Encrypt,C=US:
Unable to locally verify the issuer's authority.
```
These are often because of a server misconfiguration. Servers are
supposed to send all intermediate certificates in their certificate
chain, but sometimes they fail to do this. In that case, you need to
manually download the missing intermediate certificate from its
issuer, and install it.
You can get the information about which certificate is required from
[SSL Labs](https://www.ssllabs.com/ssltest/analyze.html). Then, by
Googling the name of the missing certificate that SSL Labs has found,
you can typically find it as a download from the certificate issuer.
Riju has a `cert` option which takes URLs of certificates to download
and install (note the use of `prepare` here because we need the
certificate when building the package, not just when installing it):
```yaml
install:
prepare:
cert:
- "https://letsencrypt.org/certs/lets-encrypt-r3.pem"
apt:
- default-jdk
manual: |
install -d "${pkg}/opt/ioke"
install -d "${pkg}/usr/local/bin"
wget https://ioke.org/dist/ioke-ikj-latest.tar.gz -O ioke.tar.gz
tar -xf ioke.tar.gz -C "${pkg}/opt/ioke" --strip-components=1
ln -s /opt/ioke/bin/ioke "${pkg}/usr/local/bin/ioke"
```
## Setting up skeleton files
Sometimes your language may require some configuration files in
addition to the actual file to be compiled. Or alternatively, your
language may do a bunch of caching work the first time it starts up,
which you don't want to happen every time someone launches it on Riju.
Both of these problems can be solved by preparing files to be copied
into the user's home directory at the start of a new Riju session
(this is done using the `setup` key, which is explained later).
In the following example, we ask .NET to create a project template for
us, and then compile the project. Then we take the files that were
created for the project, plus cache directories in `$HOME`, and copy
them into `/opt` so that they can be copied back later using `setup`.
By convention, we name such directories `skel`.
```yaml
install:
prepare: &install-dotnet
preface: |
wget "https://packages.microsoft.com/config/ubuntu/${ubuntu_ver}/packages-microsoft-prod.deb"
sudo --preserve-env=DEBIAN_FRONTEND apt-get install ./packages-microsoft-prod.deb
sudo --preserve-env=DEBIAN_FRONTEND apt-get update
apt:
- $(grep-aptavail -wF Package "dotnet-sdk-3\.[0-9.]+" -s Package -n | sort -Vr | head -n1)
<<: *install-dotnet
manual: |
install -d "${pkg}/opt/qsharp/skel-home"
install -d "${pkg}/opt/qsharp/skel-src"
dotnet new -i Microsoft.Quantum.ProjectTemplates
dotnet new console -lang Q# -o main
dotnet run --project main
shopt -s dotglob
cp -R main "${pkg}/opt/qsharp/skel-src/"
cp -R "${HOME}/.dotnet" "${HOME}/.nuget" "${pkg}/opt/qsharp/skel-home/"
rm "${pkg}/opt/qsharp/skel-src/main/Program.qs"
chmod -R a=u,go-w "${pkg}/opt/qsharp"
setup: |
shopt -s dotglob
cp -R /opt/qsharp/skel-src/* ./
cp -R /opt/qsharp/skel-home/* "${HOME}/"
```
## What about...?
Refer to [`jsonschema.yaml`](../../lib/jsonschema.yaml) for the
complete reference about what keys are accepted in `install`. See
[`generate-build-script.js`](../../tools/generate-build-scripts.js)
for how these are interpreted programmatically, and see the generated
`build.bash` and `install.bash` scripts (in `build/lang/<name>/`) for
examples of the outputs. And of course, check some of the other
languages, since probably what you want to do has come up before at
some point.