commit 52e52ba036b5d0cba128a5f5b99c7232b0270b86 Author: davidalst Date: Sun Jun 16 21:29:40 2024 +0200 first commit diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..61ebb7a Binary files /dev/null and b/.DS_Store differ diff --git a/.devcontainer/.ghci b/.devcontainer/.ghci new file mode 100644 index 0000000..ce42ab8 --- /dev/null +++ b/.devcontainer/.ghci @@ -0,0 +1,34 @@ +:set prompt "\ESC[94m\STX \ESC[m\STX" +:set prompt-cont "\ESC[1;32mλ| \ESC[m" +:set +t +:set +m +:set +s + +:set editor emacs + +:set -Wall +:set -ferror-spans +:set -freverse-errors +:set -fprint-expanded-synonyms +:set -fprint-explicit-foralls +:set -fprint-explicit-kinds +:set -ignore-package pretty-simple -package pretty-simple +-- :set -fobject-code + +:seti -XFlexibleContexts +:seti -XFlexibleInstances +:seti -XOverloadedStrings +:seti -XGADTSyntax +:seti -XGeneralizedNewtypeDeriving +:seti -XInstanceSigs +:seti -XLambdaCase +:seti -XPartialTypeSignatures +:seti -XScopedTypeVariables +:seti -XPolyKinds +:seti -XDataKinds +:seti -XTypeApplications +:seti -XTypeApplications + +:def hlint const . return $ ":! hlint \"src\"" +:def hoogle \s -> return $ ":! hoogle --count=15 \"" ++ s ++ "\"" +:def package \ m -> return $ ":! ghc-pkg --simple-output find-module " ++ m \ No newline at end of file diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..74c07f7 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,105 @@ +FROM debian:bookworm-slim as base + +ENV LANG C.UTF-8 + +ARG GHC_VERSION=9.6.3 +ARG STACK_VERSION=recommended +ARG STACK_RESOLVER=lts-22.6 +ARG CABAL_VERSION=recommended +ARG HLS_VERSION=recommended +ARG LLVM_VERSION=17 + +ENV USERNAME=vscode \ + USER_UID=1000 \ + USER_GID=1000 \ + DEBIAN_FRONTEND=noninteractive \ + GHC_VERSION=${GHC_VERSION} \ + STACK_VERSION=${STACK_VERSION} \ + STACK_RESOLVER=${STACK_RESOLVER} \ + CABAL_VERSION=${CABAL_VERSION} \ + HLS_VERSION=${HLS_VERSION} \ + LLVM_VERSION=${LLVM_VERSION} + +RUN ulimit -n 8192 + +RUN VERSION_CODENAME=$(grep VERSION_CODENAME /etc/os-release | cut -d'=' -f2) && \ + apt-get update && \ + apt-get install -y --no-install-recommends software-properties-common wget && \ + # I don't know why, nor do I have any mental capacity to figure it out, + # but we need to add the repository twice, otherwise it doesn't work (repo isn't being added) + add-apt-repository -y -s -n "deb http://apt.llvm.org/${VERSION_CODENAME}/ llvm-toolchain-${VERSION_CODENAME}-${LLVM_VERSION} main" && \ + add-apt-repository -y -s -n "deb http://apt.llvm.org/${VERSION_CODENAME}/ llvm-toolchain-${VERSION_CODENAME}-${LLVM_VERSION} main" && \ + wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc && \ + apt-get update && \ + apt-get install -y --no-install-recommends apt-utils bash build-essential ca-certificates curl gcc git gnupg libffi-dev libffi8 libgmp-dev libgmp-dev libgmp10 libicu-dev libncurses-dev libncurses5 libnuma1 libnuma-dev libtinfo5 lsb-release make procps sudo xz-utils z3 zlib1g-dev clang-$LLVM_VERSION lldb-$LLVM_VERSION lld-$LLVM_VERSION clangd-$LLVM_VERSION + +RUN groupadd --gid ${USER_GID} ${USERNAME} && \ + useradd -ms /bin/bash -K MAIL_DIR=/dev/null --uid ${USER_UID} --gid ${USER_GID} -m ${USERNAME} && \ + echo ${USERNAME} ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/${USERNAME} && \ + chmod 0440 /etc/sudoers.d/${USERNAME} + +USER ${USER_UID}:${USER_GID} +WORKDIR /home/${USERNAME} +ENV PATH="/home/${USERNAME}/.local/bin:/home/${USERNAME}/.cabal/bin:/home/${USERNAME}/.ghcup/bin:$PATH" + +RUN echo "export PATH=${PATH}" >> /home/${USERNAME}/.profile + +ENV BOOTSTRAP_HASKELL_NONINTERACTIVE=yes \ + BOOTSTRAP_HASKELL_NO_UPGRADE=yes + +FROM base as tooling + +RUN curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh + +# Set the GHC version. +RUN ghcup install ghc ${GHC_VERSION} --set + +# Install cabal-iinstall +RUN ghcup install cabal ${CABAL_VERSION} --set + +# Update Cabal. +RUN cabal update && cabal new-install cabal-install + +# Configure cabal +RUN cabal user-config update -f && \ + sed -i 's/-- ghc-options:/ghc-options: -haddock/g' ~/.cabal/config + +# Install stack +RUN ghcup install stack ${STACK_VERSION} --set + +# Set system-ghc, install-ghc and resolver for stack. +RUN ((stack ghc -- --version 2>/dev/null) || true) && \ + # Set global defaults for stack. + stack config --system-ghc set system-ghc true --global && \ + stack config --system-ghc set install-ghc false --global && \ + stack config --system-ghc set resolver ${STACK_RESOLVER} + +# Set global custom defaults for stack. +RUN printf "ghc-options:\n \"\$everything\": -haddock\n" >> /home/${USERNAME}/.stack/config.yaml + +# Install hls +RUN ghcup install hls ${HLS_VERSION} --set + +FROM tooling as packages + +# Install global packages. +# Versions are pinned, since we don't want to accidentally break anything (by always installing latest). +RUN cabal install --haddock-hoogle --minimize-conflict-set \ + fsnotify \ + haskell-dap \ + ghci-dap \ + haskell-debug-adapter \ + hlint \ + apply-refact \ + retrie \ + hoogle \ + ormolu + +FROM packages as hoogle + +# Generate hoogle db +RUN hoogle generate --download --haskell + +ENV DEBIAN_FRONTEND=dialog + +ENTRYPOINT ["/bin/bash"] diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..6781ee1 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,51 @@ +{ + "name": "DevContainer for Haskell (GHC, Stack, Cabal, HIE, LSP, DAP, etc.)", + "remoteUser": "vscode", + "runArgs": [], + "build": { + "args": { + "USERNAME": "vscode", + "GHC_VERSION": "9.6.3", + "STACK_VERSION": "recommended", + "STACK_RESOLVER": "lts-22.6", + "CABAL_VERSION": "recommended", + "HLS_VERSION": "recommended", + "LLVM_VERSION": "17" + }, + "context": "..", + "dockerfile": "Dockerfile" + }, + "customizations": { + "vscode": { + "extensions": [ + "haskell.haskell", + "phoityne.phoityne-vscode", + "eriksik2.vscode-ghci", + "jcanero.hoogle-vscode" + ], + "settings": { + "files.exclude": { + "**/*.olean": true, + "**/.DS_Store": true, + "**/.git": true, + "**/.hg": true, + "**/.svn": true, + "**/CVS": true + }, + "haskell.checkProject": true, + "haskell.formattingProvider": "ormolu", + "haskell.indentationRules.enabled": true, + "haskell.liquidOn": false, + "haskell.checkParents": "CheckOnSave", + "haskell.manageHLS": "GHCup", + "haskell.maxCompletions": 40, + "haskell.openDocumentationInHackage": false, + "haskell.openSourceInHackage": false, + "haskell.trace.client": "error", + "haskell.trace.server": "off", + "haskell.upgradeGHCup": true, + "hoogle-vscode.useCabalDependencies": true + } + } + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..c7efda6 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,50 @@ + +{ + // Automatically created by phoityne-vscode extension. + + "version": "2.0.0", + "presentation": { + "reveal": "always", + "panel": "new" + }, + "tasks": [ + { + // F7 + "group": { + "kind": "build", + "isDefault": true + }, + "label": "haskell build", + "type": "shell", + //"command": "cabal configure && cabal build" + "command": "stack build" + }, + { + // F6 + "group": "build", + "type": "shell", + "label": "haskell clean & build", + //"command": "cabal clean && cabal configure && cabal build" + "command": "stack clean && stack build" + //"command": "stack clean ; stack build" // for powershell + }, + { + // F8 + "group": { + "kind": "test", + "isDefault": true + }, + "type": "shell", + "label": "haskell test", + //"command": "cabal test" + "command": "stack test" + }, + { + // F6 + "isBackground": true, + "type": "shell", + "label": "haskell watch", + "command": "stack build --test --no-run-tests --file-watch" + } + ] +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..df30fc2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Igal Tabachnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7e2773c --- /dev/null +++ b/README.md @@ -0,0 +1,164 @@ +# A (highly opinionated) Docker image for Haskell development + +**Note**: The way this container setup is very opinionaed, you may want to check out [an official dev containter for Haskell](https://github.com/microsoft/vscode-dev-containers/tree/main/containers/haskell). + + +**Table of Contents** + +- [A (highly opinionated) Docker image for Haskell development](#a-highly-opinionated-docker-image-for-haskell-development) + - [Visual Studio Code: DevContainer for Haskell](#visual-studio-code-devcontainer-for-haskell) + - [What is this](#what-is-this) + - [How to use this](#how-to-use-this) + - [How does it work](#how-does-it-work) + - [What's in the box](#whats-in-the-box) + - [How to build locally](#how-to-build-locally) + + + +## Visual Studio Code: DevContainer for Haskell + +### What is this + +This is a DevContainer [[1](https://code.visualstudio.com/docs/remote/containers)][[2](https://code.visualstudio.com/docs/remote/containers-advanced)] environment for Visual Studio Code, allowing automatically installing the Haskell compiler (GHC), Stack, Cabal, HLS (Haskell Language Server), and the necessary Visual Studio Code extensions to set up a Haskell development environment with zero additional effort. + +**Note**: For debugging support, please refer to [Haskell GHCi Debugger Adapter Phoityne](https://marketplace.visualstudio.com/items?itemName=phoityne.phoityne-vscode) extension documentation. + +### How to use this + +Follow the [Getting Started](https://code.visualstudio.com/docs/remote/containers#_getting-started) instructions to configure your Visual Studio Code and Docker to use with DevContainers. + +Place the `.devcontainer` directory in the root of your project, and the next time you load the project, Visual Studio Code will prompt to re-open the project in a container: + +![image](https://user-images.githubusercontent.com/601206/73298150-7bfac580-4215-11ea-81d3-a8fabab98e30.png) + +**Note**: building the container might take a few minutes until all dependencies have finished downloading and installing. + +### How does it work + +Visual Studio Code supports [Developing inside a Container](https://code.visualstudio.com/docs/remote/containers) - using a Docker image as a development environment. It automates the process of creating the container image, as well as installing additional required extensions into the editor. + +Pressing **Reopen in Container** will perform the automated steps to launch the container, and set up the environment. + +For more information and setup, read the official documentation: and + +## What's in the box + +[`.devcontainer/Dockerfile`](.devcontainer/Dockerfile) DevContainer Docker image. + +[`debian:bullseye`](https://hub.docker.com/_/debian) as a base image. + +Additional software installed: + +- Glasgow Haskell Compiler (GHC) via [ghcup](https://www.haskell.org/ghcup/). +- A Haskell LSP server HLS - ([haskell-language-server](https://github.com/haskell/haskell-language-server)). +- [Stack](https://docs.haskellstack.org/en/stable/README/). +- [Cabal](https://www.haskell.org/cabal/). +- Following Debian packages: `apt-utils bash build-essential ca-certificates curl gcc git gnupg libffi-dev libffi7 libgmp-dev libgmp-dev libgmp10 libicu-dev libncurses-dev libncurses5 libnuma1 libnuma-dev libtinfo5 lsb-release make procps software-properties-common sudo wget xz-utils z3 zlib1g-dev`. +- Following Haskell packages (via [cabal](https://nixos.org/nixos/packages.html)): `haskell-dap ghci-dap haskell-debug-adapter hlint apply-refact retrie stylish-haskell hoogle ormolu liquidhaskell`. +- LLVM 12 via [LLVM Automatic installation script](https://apt.llvm.org/). + +**NOTE:** *When changing GHC/HLS versions, please refer to the [GHC/HLS compatibility table](https://haskell-language-server.readthedocs.io/en/latest/supported-versions.html).* + +Following VSCode extensions are installed after container is started: + +- [Haskell extension](https://marketplace.visualstudio.com/items?itemName=haskell.haskell). +- [Haskell GHCi Debugger Adapter](https://marketplace.visualstudio.com/items?itemName=phoityne.phoityne-vscode). +- [Integrated Haskell Shell](https://marketplace.visualstudio.com/items?itemName=eriksik2.vscode-ghci). +- [Hoogle for VSCode](https://marketplace.visualstudio.com/items?itemName=jcanero.hoogle-vscode). + +The [`devcontainer.json`](.devcontainer/devcontainer.json) has some additional configuration for VSCode, in particular, the required extensions that have to be installed, the name of the remote user (must match the one in the [`Dockerfile`](.devcontainer/Dockerfile)), and...⏎ + +
+... the following settings + +```json +{ + "files.exclude": { + "**/*.olean": true, + "**/.DS_Store": true, + "**/.git": true, + "**/.hg": true, + "**/.svn": true, + "**/CVS": true + }, + "haskell.checkProject": true, + "haskell.formattingProvider": "ormolu", + "haskell.indentationRules.enabled": true, + "haskell.liquidOn": true, + "haskell.manageHLS": "GHCup", + "haskell.maxCompletions": 40, + "haskell.openDocumentationInHackage": false, + "haskell.openSourceInHackage": false, + "haskell.plugin.alternateNumberFormat.globalOn": true, + "haskell.plugin.callHierarchy.globalOn": true, + "haskell.plugin.changeTypeSignature.globalOn": true, + "haskell.plugin.class.globalOn": true, + "haskell.plugin.eval.config.diff": true, + "haskell.plugin.eval.config.exception": true, + "haskell.plugin.eval.globalOn": true, + "haskell.plugin.ghcide-code-actions-bindings.globalOn": true, + "haskell.plugin.ghcide-code-actions-fill-holes.globalOn": true, + "haskell.plugin.ghcide-code-actions-imports-exports.globalOn": true, + "haskell.plugin.ghcide-code-actions-type-signatures.globalOn": true, + "haskell.plugin.ghcide-completions.config.autoExtendOn": true, + "haskell.plugin.ghcide-completions.config.snippetsOn": true, + "haskell.plugin.ghcide-completions.globalOn": true, + "haskell.plugin.ghcide-hover-and-symbols.hoverOn": true, + "haskell.plugin.ghcide-hover-and-symbols.symbolsOn": true, + "haskell.plugin.ghcide-type-lenses.config.mode": "always", + "haskell.plugin.ghcide-type-lenses.globalOn": true, + "haskell.plugin.haddockComments.globalOn": true, + "haskell.plugin.hlint.codeActionsOn": true, + "haskell.plugin.hlint.config.flags": [], + "haskell.plugin.hlint.diagnosticsOn": true, + "haskell.plugin.importLens.codeActionsOn": true, + "haskell.plugin.importLens.codeLensOn": true, + "haskell.plugin.moduleName.globalOn": true, + "haskell.plugin.pragmas.codeActionsOn": true, + "haskell.plugin.pragmas.completionOn": true, + "haskell.plugin.qualifyImportedNames.globalOn": true, + "haskell.plugin.refineImports.codeActionsOn": true, + "haskell.plugin.refineImports.codeLensOn": true, + "haskell.plugin.refineImports.globalOn": true, + "haskell.plugin.rename.config.crossModule": true, + "haskell.plugin.rename.globalOn": true, + "haskell.plugin.retrie.globalOn": true, + "haskell.plugin.splice.globalOn": true, + "haskell.plugin.tactic.config.max_use_ctor_actions": 5, + "haskell.plugin.tactics.codeActionsOn": true, + "haskell.plugin.tactics.codeLensOn": true, + "haskell.plugin.tactics.config.auto_gas": 4, + "haskell.plugin.tactics.config.hole_severity": "hint", + "haskell.plugin.tactics.config.max_use_ctor_actions": 5, + "haskell.plugin.tactics.config.proofstate_styling": true, + "haskell.plugin.tactics.config.timeout_duration": 5, + "haskell.plugin.tactics.globalOn": true, + "haskell.plugin.tactics.hoverOn": true, + "haskell.trace.client": "error", + "haskell.trace.server": "off", + "haskell.upgradeGHCup": true, + "hoogle-vscode.useCabalDependencies": true +} +``` + +
+ +This was initially based on the [DevContainer for HIE (Haskell IDE Engine)](https://github.com/hmemcpy/haskell-hie-devcontainer) by [Igal Tabachnik](https://github.com/hmemcpy). + +### How to build locally + +To build the image locally, just run `docker build ./.devcontainer/ --file ./.devcontainer/Dockerfile --tag haskelldevenv:latest` in the repository root. + +You can override GHC version, Cabal version, Stack resolver and HLS version by specifying `GHC_VERSION=...`, `CABAL_VERSION=...`, `STACK_RESOLVER=...` and `HLS_VERSION=...` respectively as build args. + +For example: + +- `docker build ./.devcontainer/ --file ./.devcontainer/Dockerfile --tag haskelldevenv:latest --build-arg GHC_VERSION=8.10.7` +- `docker build ./.devcontainer/ --file ./.devcontainer/Dockerfile --tag haskelldevenv:latest --build-arg STACK_RESOLVER=lts-18.13` +- `docker build ./.devcontainer/ --file ./.devcontainer/Dockerfile --tag haskelldevenv:latest --build-arg CABAL_VERSION=3.6.2.0` +- `docker build ./.devcontainer/ --file ./.devcontainer/Dockerfile --tag haskelldevenv:latest --build-arg HLS_VERSION=1.5.1` +- `docker build ./.devcontainer/ --file ./.devcontainer/Dockerfile --tag haskelldevenv:latest --build-arg GHC_VERSION=8.8.4 --build-arg STACK_RESOLVER=lts-18.13 --build-arg CABAL_VERSION=3.6.2.0 --build-arg HLS_VERSION=1.5.1` + +To run the image, just do `docker run --rm -it haskelldevenv:latest`. + +Valid values for `GHC_VERSION` can be found [here](https://www.haskell.org/ghc/download.html). diff --git a/programs/hello b/programs/hello new file mode 100755 index 0000000..9a0cc28 Binary files /dev/null and b/programs/hello differ diff --git a/programs/hello.hi b/programs/hello.hi new file mode 100644 index 0000000..7d823c3 Binary files /dev/null and b/programs/hello.hi differ diff --git a/programs/hello.hs b/programs/hello.hs new file mode 100644 index 0000000..f6f8553 --- /dev/null +++ b/programs/hello.hs @@ -0,0 +1,3 @@ +main = do + putStrLn "Hello, everybody!" + putStrLn ("Please look at my favorite odd numbers: " ++ show (filter odd [10..20])) \ No newline at end of file diff --git a/programs/hello.o b/programs/hello.o new file mode 100644 index 0000000..781e2ae Binary files /dev/null and b/programs/hello.o differ