-
-
Notifications
You must be signed in to change notification settings - Fork 14.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Agda rework #76653
Merged
Merged
Agda rework #76653
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
229ef0e
TotalParserCombinators: remove broken package
alexarice 99fa064
Agda-Sheaves: remove broken package
alexarice 781a927
pretty: remove broken package
alexarice 36840c5
categories: remove broken package
alexarice a519f22
bitvector: remove broken package
alexarice 6cbaa25
agda-base: remove broken package
alexarice d30e246
agda: rework builder
alexarice c2814be
agda-prelude: update
alexarice 6c1cded
agda-categories: init at 0.1
alexarice a7cd372
agda.standard-library: 1.1 -> 1.3
alexarice 1175065
agda.iowa-stdlib: mark broken
alexarice b78a5a0
agda: Added test
turion 43fb96e
iowa-stdlib: update homepage URL
kini 8ee4c36
Agda: Add turion as maintainer
alexarice File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
--- | ||
title: Agda | ||
author: Alex Rice (alexarice) | ||
date: 2020-01-06 | ||
--- | ||
# Agda | ||
|
||
## How to use Agda | ||
|
||
Agda can be installed from `agda`: | ||
``` | ||
$ nix-env -iA agda | ||
``` | ||
|
||
To use agda with libraries, the `agda.withPackages` function can be used. This function either takes: | ||
+ A list of packages, | ||
+ or a function which returns a list of packages when given the `agdaPackages` attribute set, | ||
+ or an attribute set containing a list of packages and a GHC derivation for compilation (see below). | ||
|
||
For example, suppose we wanted a version of agda which has access to the standard library. This can be obtained with the expressions: | ||
|
||
``` | ||
agda.withPackages [ agdaPackages.standard-library ] | ||
``` | ||
|
||
or | ||
|
||
``` | ||
agda.withPackages (p: [ p.standard-library ]) | ||
``` | ||
|
||
or can be called as in the [Compiling Agda](#compiling-agda) section. | ||
|
||
If you want to use a library in your home directory (for instance if it is a development version) then typecheck it manually (using `agda.withPackages` if necessary) and then override the `src` attribute of the package to point to your local repository. | ||
|
||
Agda will not by default use these libraries. To tell agda to use the library we have some options: | ||
- Call `agda` with the library flag: | ||
``` | ||
$ agda -l standard-library -i . MyFile.agda | ||
alexarice marked this conversation as resolved.
Show resolved
Hide resolved
|
||
``` | ||
- Write a `my-library.agda-lib` file for the project you are working on which may look like: | ||
``` | ||
name: my-library | ||
include: . | ||
depends: standard-library | ||
``` | ||
- Create the file `~/.agda/defaults` and add any libraries you want to use by default. | ||
|
||
More information can be found in the [official Agda documentation on library management](https://agda.readthedocs.io/en/v2.6.1/tools/package-system.html). | ||
|
||
## Compiling Agda | ||
Agda modules can be compiled with the `--compile` flag. A version of `ghc` with `ieee` is made available to the Agda program via the `--with-compiler` flag. | ||
This can be overridden by a different version of `ghc` as follows: | ||
|
||
``` | ||
agda.withPackages { | ||
pkgs = [ ... ]; | ||
ghc = haskell.compiler.ghcHEAD; | ||
} | ||
``` | ||
|
||
## Writing Agda packages | ||
To write a nix derivation for an agda library, first check that the library has a `*.agda-lib` file. | ||
|
||
A derivation can then be written using `agdaPackages.mkDerivation`. This has similar arguments to `stdenv.mkDerivation` with the following additions: | ||
+ `everythingFile` can be used to specify the location of the `Everything.agda` file, defaulting to `./Everything.agda`. If this file does not exist then either it should be patched in or the `buildPhase` should be overridden (see below). | ||
+ `libraryName` should be the name that appears in the `*.agda-lib` file, defaulting to `pname`. | ||
+ `libraryFile` should be the file name of the `*.agda-lib` file, defaulting to `${libraryName}.agda-lib`. | ||
alexarice marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The build phase for `agdaPackages.mkDerivation` simply runs `agda` on the `Everything.agda` file. If something else is needed to build the package (e.g. `make`) then the `buildPhase` should be overridden (or a `preBuild` or `configurePhase` can be used if there are steps that need to be done prior to checking the `Everything.agda` file). `agda` and the Agda libraries contained in `buildInputs` are made available during the build phase. The install phase simply copies all `.agda`, `.agdai` and `.agda-lib` files to the output directory. Again, this can be overridden. | ||
|
||
To add an agda package to `nixpkgs`, the derivation should be written to `pkgs/development/libraries/agda/${library-name}/` and an entry should be added to `pkgs/top-level/agda-packages.nix`. Here it is called in a scope with access to all other agda libraries, so the top line of the `default.nix` can look like: | ||
``` | ||
{ mkDerivation, standard-library, fetchFromGitHub }: | ||
alexarice marked this conversation as resolved.
Show resolved
Hide resolved
|
||
``` | ||
and `mkDerivation` should be called instead of `agdaPackages.mkDerivation`. Here is an example skeleton derivation for iowa-stdlib: | ||
|
||
``` | ||
mkDerivation { | ||
version = "1.5.0"; | ||
pname = "iowa-stdlib"; | ||
|
||
src = ... | ||
|
||
libraryFile = ""; | ||
libraryName = "IAL-1.3"; | ||
|
||
buildPhase = '' | ||
patchShebangs find-deps.sh | ||
make | ||
''; | ||
} | ||
``` | ||
This library has a file called `.agda-lib`, and so we give an empty string to `libraryFile` as nothing precedes `.agda-lib` in the filename. This file contains `name: IAL-1.3`, and so we let `libraryName = "IAL-1.3"`. This library does not use an `Everything.agda` file and instead has a Makefile, so there is no need to set `everythingFile` and we set a custom `buildPhase`. | ||
|
||
When writing an agda package it is essential to make sure that no `.agda-lib` file gets added to the store as a single file (for example by using `writeText`). This causes agda to think that the nix store is a agda library and it will attempt to write to it whenever it typechecks something. See [https://github.com/agda/agda/issues/4613](https://github.com/agda/agda/issues/4613). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import ./make-test-python.nix ({ pkgs, ... }: | ||
|
||
let | ||
hello-world = pkgs.writeText "hello-world" '' | ||
open import IO | ||
|
||
main = run(putStrLn "Hello World!") | ||
''; | ||
in | ||
{ | ||
name = "agda"; | ||
meta = with pkgs.stdenv.lib.maintainers; { | ||
maintainers = [ alexarice turion ]; | ||
}; | ||
|
||
machine = { pkgs, ... }: { | ||
environment.systemPackages = [ | ||
(pkgs.agda.withPackages { | ||
pkgs = p: [ p.standard-library ]; | ||
}) | ||
]; | ||
virtualisation.memorySize = 2000; # Agda uses a lot of memory | ||
}; | ||
|
||
testScript = '' | ||
# Minimal script that typechecks | ||
machine.succeed("touch TestEmpty.agda") | ||
machine.succeed("agda TestEmpty.agda") | ||
|
||
# Minimal script that actually uses the standard library | ||
machine.succeed('echo "import IO" > TestIO.agda') | ||
machine.succeed("agda -l standard-library -i . TestIO.agda") | ||
|
||
# # Hello world | ||
machine.succeed( | ||
"cp ${hello-world} HelloWorld.agda" | ||
) | ||
machine.succeed("agda -l standard-library -i . -c HelloWorld.agda") | ||
''; | ||
} | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,90 +1,71 @@ | ||
# Builder for Agda packages. Mostly inspired by the cabal builder. | ||
# Builder for Agda packages. | ||
|
||
{ stdenv, Agda, glibcLocales | ||
, writeShellScriptBin | ||
, extension ? (self: super: {}) | ||
}: | ||
{ stdenv, lib, self, Agda, runCommandNoCC, makeWrapper, writeText, mkShell, ghcWithPackages }: | ||
|
||
with stdenv.lib.strings; | ||
with lib.strings; | ||
|
||
let | ||
defaults = self : { | ||
# There is no Hackage for Agda so we require src. | ||
inherit (self) src name; | ||
|
||
isAgdaPackage = true; | ||
|
||
buildInputs = [ Agda ] ++ self.buildDepends; | ||
buildDepends = []; | ||
|
||
buildDependsAgda = stdenv.lib.filter | ||
(dep: dep ? isAgdaPackage && dep.isAgdaPackage) | ||
self.buildDepends; | ||
buildDependsAgdaShareAgda = map (x: x + "/share/agda") self.buildDependsAgda; | ||
|
||
# Not much choice here ;) | ||
LANG = "en_US.UTF-8"; | ||
LOCALE_ARCHIVE = stdenv.lib.optionalString | ||
stdenv.isLinux | ||
"${glibcLocales}/lib/locale/locale-archive"; | ||
|
||
everythingFile = "Everything.agda"; | ||
|
||
propagatedBuildInputs = self.buildDependsAgda; | ||
propagatedUserEnvPkgs = self.buildDependsAgda; | ||
|
||
# Immediate source directories under which modules can be found. | ||
sourceDirectories = [ ]; | ||
|
||
# This is used if we have a top-level element that only serves | ||
# as the container for the source and we only care about its | ||
# contents. The directories put here will have their | ||
# *contents* copied over as opposed to sourceDirectories which | ||
# would make a direct copy of the whole thing. | ||
topSourceDirectories = [ "src" ]; | ||
|
||
# FIXME: `dirOf self.everythingFile` is what we really want, not hardcoded "./" | ||
includeDirs = self.buildDependsAgdaShareAgda | ||
++ self.sourceDirectories ++ self.topSourceDirectories | ||
++ [ "." ]; | ||
buildFlags = stdenv.lib.concatMap (x: ["-i" x]) self.includeDirs; | ||
|
||
agdaWithArgs = "${Agda}/bin/agda ${toString self.buildFlags}"; | ||
|
||
buildPhase = '' | ||
runHook preBuild | ||
${self.agdaWithArgs} ${self.everythingFile} | ||
runHook postBuild | ||
withPackages' = { | ||
pkgs, | ||
ghc ? ghcWithPackages (p: with p; [ ieee ]) | ||
}: let | ||
pkgs' = if builtins.isList pkgs then pkgs else pkgs self; | ||
library-file = writeText "libraries" '' | ||
${(concatMapStringsSep "\n" (p: "${p}/${p.libraryFile}") pkgs')} | ||
''; | ||
|
||
installPhase = let | ||
srcFiles = self.sourceDirectories | ||
++ map (x: x + "/*") self.topSourceDirectories; | ||
in '' | ||
runHook preInstall | ||
mkdir -p $out/share/agda | ||
cp -pR ${concatStringsSep " " srcFiles} $out/share/agda | ||
runHook postInstall | ||
''; | ||
|
||
passthru = { | ||
env = stdenv.mkDerivation { | ||
name = "interactive-${self.name}"; | ||
inherit (self) LANG LOCALE_ARCHIVE; | ||
AGDA_PACKAGE_PATH = concatMapStrings (x: x + ":") self.buildDependsAgdaShareAgda; | ||
buildInputs = let | ||
# Makes a wrapper available to the user. Very useful in | ||
# nix-shell where all dependencies are -i'd. | ||
agdaWrapper = writeShellScriptBin "agda" '' | ||
exec ${self.agdaWithArgs} "$@" | ||
''; | ||
in [agdaWrapper] ++ self.buildDepends; | ||
pname = "agdaWithPackages"; | ||
version = Agda.version; | ||
in runCommandNoCC "${pname}-${version}" { | ||
inherit pname version; | ||
nativeBuildInputs = [ makeWrapper ]; | ||
alexarice marked this conversation as resolved.
Show resolved
Hide resolved
|
||
passthru.unwrapped = Agda; | ||
} '' | ||
mkdir -p $out/bin | ||
makeWrapper ${Agda}/bin/agda $out/bin/agda \ | ||
--add-flags "--with-compiler=${ghc}/bin/ghc" \ | ||
--add-flags "--library-file=${library-file}" \ | ||
--add-flags "--local-interfaces" | ||
makeWrapper ${Agda}/bin/agda-mode $out/bin/agda-mode | ||
''; # Local interfaces has been added for now: See https://github.com/agda/agda/issues/4526 | ||
|
||
withPackages = arg: if builtins.isAttrs arg then withPackages' arg else withPackages' { pkgs = arg; }; | ||
|
||
|
||
defaults = | ||
{ pname | ||
, buildInputs ? [] | ||
, everythingFile ? "./Everything.agda" | ||
, libraryName ? pname | ||
, libraryFile ? "${libraryName}.agda-lib" | ||
, buildPhase ? null | ||
, installPhase ? null | ||
, ... | ||
}: let | ||
agdaWithArgs = withPackages (builtins.filter (p: p ? isAgdaDerivation) buildInputs); | ||
in | ||
{ | ||
inherit libraryName libraryFile; | ||
|
||
isAgdaDerivation = true; | ||
|
||
buildInputs = buildInputs ++ [ agdaWithArgs ]; | ||
|
||
buildPhase = if buildPhase != null then buildPhase else '' | ||
runHook preBuild | ||
agda -i ${dirOf everythingFile} ${everythingFile} | ||
runHook postBuild | ||
''; | ||
|
||
installPhase = if installPhase != null then installPhase else '' | ||
runHook preInstall | ||
mkdir -p $out | ||
find \( -name '*.agda' -or -name '*.agdai' -or -name '*.agda-lib' \) -exec cp -p --parents -t "$out" {} + | ||
runHook postInstall | ||
''; | ||
}; | ||
}; | ||
}; | ||
in | ||
{ mkDerivation = args: let | ||
super = defaults self // args self; | ||
self = super // extension self super; | ||
in stdenv.mkDerivation self; | ||
{ | ||
mkDerivation = args: stdenv.mkDerivation (args // defaults args); | ||
alexarice marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
inherit withPackages withPackages'; | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this needs to be changed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. I think non-agda dependencies don't belong to the
withPackages
interface.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More just looking at the file it looks like this changes it to say the same thing twice but maybe I am not understanding the diff
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alexarice
No, it's not the same. It's meant to cover case when:
but again, this interface should not exist in the first place IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see, this is just adding an extra thing. I still don't think it is necessary as it occurs below as well. I'm not sure what you mean about the interface should not exist?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I wrote (with
ghc
argument) occurs below. But the suggested option without ghc is just longer, and doesn't add anything.I was thinking that
would be overridable as
(agda.withPackages (p: [p.standard-library])).override { ghc = haskell.compiler.ghcHEAD; }
.This, of course, depends on how often you need to override
ghc
. I was assuming this was not needed often, but #76653 (comment) mentions Haskell FFI, so, perhaps, this may be a needed option.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My reasoning to add this is to erradicate the difference in the docs where it first says "there are three ways to do libraries", and then only two examples are shown.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The solution here seems to be changing the 3 to a 2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah nevermind I see what you mean now