Giter VIP home page Giter VIP logo

rules_haskell's Introduction

rules_haskell

Haskell rules for Bazel

Continuous Integration

Bazel CI: Build status

Bazel automates building and testing software. It scales to very large multi-language projects. This project extends Bazel with build rules for Haskell. Get started building your own project using these rules with the setup script below.

Rule summary

The full reference documentation for rules is at https://haskell.build.

Setup

You'll need Bazel >= 6.0 installed.

If you are on NixOS, skip to the Nixpkgs section.

System dependencies

Refer to the "Before you begin" section in the documentation.

The easy way

In a fresh directory, run:

$ curl https://haskell.build/start | sh

Alternatively, if you want to start a project with bzlmod, run:

$ sh <(curl https://haskell.build/start) --with-bzlmod=true

This will generate initial WORKSPACE and BUILD files for you. See the examples and the API reference below to adapt these for your project. Then,

$ bazel build //...    # Build all targets
$ bazel test //...     # Run all tests

You can learn more about Bazel's command line syntax here. Common commands are build, test, run and coverage.

Nixpkgs

This rule set supports using Nixpkgs to provision your GHC toolchain and to fetch hackage packages from there. To create your project, pass --use-nix, like so:

$ sh <(curl https://haskell.build/start) --use-nix

This generates the same files as above, but uses nixpkgs to provision GHC.

If you are on NixOS, this is the only way to set up your project, because the GHC toolchain provisioned through binary distributions cannot be executed on NixOS.

Tutorial and Examples

We provide a tutorial for writing your first rules. The corresponding source code is in ./tutorial.

A collection of example rules is in ./examples.

Rules

See https://api.haskell.build for the reference documentation on provided rules. Using ./serve-docs.sh, you can also view this documentation locally.

Troubleshooting

No such file or directory

If you see error messages complaining about missing as (ld or indeed some other executable):

cc: error trying to exec 'as': execvp: No such file or directory
`cc' failed in phase `Assembler'. (Exit code: 1)

It means that your gcc cannot find as by itself. This happens only on certain operating systems which have gcc compiled without --with-as and --with-ld flags. We need to make as visible manually in that case:

# Create a symlink to system executable 'as'
genrule(
    name = "toolchain_as",
    outs = ["as"],
    cmd = "ln -s /usr/bin/as $@",
)

# Make it visible to rules_haskell rules:
haskell_toolchain(
    name = "ghc",
    tools = ["@ghc//:bin"],
    version = "8.4.1",
    extra_binaries = [":toolchain_as"], # <----
)

__STDC_VERSION__ does not advertise C99 or later

If you see an error message like this:

/root/.cache/bazel/_bazel_root/b8b1b1d6144a88c698a010767d2217af/external/ghc/lib/ghc-8.4.1/include/Stg.h:29:3: error:
     error: #error __STDC_VERSION__ does not advertise C99 or later
     # error __STDC_VERSION__ does not advertise C99 or later
       ^
   |
29 | # error __STDC_VERSION__ does not advertise C99 or later
   |   ^

It means that your gcc selects incorrect flavor of C by default. We need C99 or later, as the error message says, so try this:

haskell_toolchain(
    name = "ghc",
    tools = ["@ghc//:bin"],
    version = "8.4.1",
    compiler_flags = ["-optc-std=c99"], # <----
)

bazel fails because some executable cannot be found

Make sure you run your build in a pure nix shell (nix-shell --pure shell.nix). If it still doesn’t build, it is likely a bug.

A Haskell dependency fails with strange error messages

If you get cabal error messages the likes of:

CallStack (from HasCallStack):
  dieNoWrap, called at libraries/Cabal/Cabal/Distribution/Utils/LogProgress.hs:61:9 in Cabal-2.0.1.0:Distribution.Utils.LogProgress
Error:
    The following packages are broken because other packages they depend on are missing. These broken packages must be rebuilt before they can be used.
installed package lens-labels-0.2.0.1 is broken due to missing package profunctors-5.2.2-HzcVdviprlKb7Ap1woZu4, tagged-0.8.5-HviTdonkllN1ZD6he1Zn8I

you’ve most likely hit GHC’s infamous non-deterministic library ID bug.

Warning about home modules during non-sandboxed builds

Say you have a folder that mixes source files for two different libraries or for a library and an executable. If you build with sandboxing turned off, it is possible that GHC will use the source files for one library during the build of the other. The danger in this situation is that because GHC used inputs that Bazel didn't know about, incremental rebuilds might not be correct. This is why you get a warning of the following form if this happens:

<no location info>: warning: [-Wmissing-home-modules]
    Modules are not listed in command line but needed for compilation: Foo

Turning sandboxing on (this is Bazel's default on Linux and macOS) protects against this problem. If sandboxing is not an option, simply put the source files for each target in a separate directory (you can still use a single BUILD file to define all targets).

hGetContents: invalid argument (invalid byte sequence)

If you are using the GHC bindists and see an error message like this:

haddock: internal error: /tmp/tmputn68mya/doc/html/path-io/haddock-response300-1.txt: hGetContents: invalid argument (invalid byte sequence)

It means that the default locale (C.UTF-8) does not work on your system. You can use a locale that your system has. For example, if your system has the locale en_US.UTF-8, you can specify that locale:

rules_haskell_toolchains(
    locale = "en_US.UTF-8", # <----
    version = "8.4.1",
)

To find available locales, run locale -a in a terminal. You should see output like the following:

$ locale -a
C
en_US
en_US.iso88591
en_US.utf8
POSIX

Windows: Incorrect cc_toolchain used

If you're using Windows, bazel might use a different cc_toolchain than is required to build. This might happen if the environment has a cc_toolchain from Visual Studio. This might show up with an error like:

Traceback (most recent call last):
  File "\\?\C:\Users\appveyor\AppData\Local\Temp\1\Bazel.runfiles_w5rfpqk5\runfiles\rules_haskell\haskell\cabal_wrapper.py", line 105, in <module>
    strip = find_exe("external/local_config_cc/wrapper/bin/msvc_nop.bat")
  File "\\?\C:\Users\appveyor\AppData\Local\Temp\1\Bazel.runfiles_w5rfpqk5\runfiles\rules_haskell\haskell\cabal_wrapper.py", line 56, in find_exe
    if not os.path.isfile(path) and "True" == "True":
  File "C:\Python37\lib\genericpath.py", line 30, in isfile
    st = os.stat(path)
TypeError: stat: path should be string, bytes, os.PathLike or integer, not NoneType

You can override the cc_toolchain chosen with the following flag:

--crosstool_top=@rules_haskell_ghc_windows_amd64//:cc_toolchain

This chooses the cc_toolchain bundled with GHC.

GHC settings file does not exist

If you use the GHC bindist toolchain, i.e. haskell_register_ghc_bindists, then you may encounter the following type of error with packages that use the GHC API, e.g. doctest, ghcide, or proto-lens-protoc:

.../lib/settings: openFile: does not exist (No such file or directory)

This could be caused by a dependency on the ghc-paths package which bakes the path to the GHC installation at build time into the binary. In the GHC bindist use case this path points into Bazel's sandbox working directory which can change between build actions and between build-time and runtime.

You can use @rules_haskell//tools/ghc-paths as a drop-in replacement to work around this issue. See tools/ghc-paths/README.md for further details.

Windows: protoc.exe exits with an error

If you see

protoc.exe: error while loading shared libraries: api-ms-win-crt-filesystem-l1-1-0.dll: cannot open shared object file: No such file or directory

or

Process finished with exit code -1073741515 (0xC0000135)

this usually means the executable cannot find a DLL it depends on (not necessarily the DLL that is mentioned in the error message).

Newer Windows GHC distributions (>= 9.4), come with clang as the C/C++ compiler, and executables produced using that toolchain depend on the libc++ DLL, which is found in the mingw\bin directory of the bindist. You can pass --proto_compiler @rules_haskell//tests:protoc as a build flag to bazel as a workaround (see [tests/protoc.bzl]).

For rules_haskell developers

Configuring your platform

rules_haskell can be built and tested on Linux, MacOS, and Windows. Depending on the platform GHC can be provisioned using nixpkgs or by downloading a binary distribution. In case of nixpkgs other toolchains (C compiler, Python, shell tools) will also be provided by nixpkgs, in case of bindist they will be taken from the environment ($PATH). The following --config options select the corresponding combination of operating system and GHC distribution:

Linux MacOS Windows
nixpkgs linux-nixpkgs macos-nixpkgs
binary distribution linux-bindist macos-bindist windows-bindist

Hint: You can use Bazel's --announce_rc flag to see what options are being used for a command in a specific configuration. E.g.

$ bazel build //tests:run-tests --config linux-nixpkgs --nobuild --announce_rc

Hint: To avoid repetition you can add your configuration to .bazelrc.local.

echo "build --config=linux-nixpkgs" >>.bazelrc.local

Saving common command-line flags to a file

If you find yourself constantly passing the same flags on the command-line for certain commands (such as --host_platform or --compiler), you can augment the .bazelrc file in this repository with a .bazelrc.local file. This file is ignored by Git.

Reference a local checkout of rules_haskell

When you develop on rules_haskell, you usually do it in the context of a different project that has rules_haskell as a WORKSPACE dependency, like so:

http_archive(
    name = "rules_haskell",
    strip_prefix = "rules_haskell-" + version,
    sha256 = …,
    urls = …,
)

To reference a local checkout instead, use the --override_repository command line option:

bazel build/test/run/sync \
  --override_repository rules_haskell=/path/to/checkout

If you don’t want to type that every time, temporarily add it to .bazelrc.

Test Suite

To run the test suite for these rules, you'll need Nix installed. First, from the project’s folder start a pure nix shell:

$ nix-shell --pure shell.nix

This will make sure that bazel has the exact same environment on every development system (python, ghc, go, …).

To build and run tests locally, execute:

$ bazel test //... && cd rules_haskell_tests && bazel test //...

Starlark code in this project is formatted according to the output of buildifier. You can check that the formatting is correct using:

$ bazel run //buildifier && cd rules_haskell_tests && bazel run //buildifier

If tests fail then run the following to fix the formatting:

$ git rebase --exec "bazel run //buildifier:buildifier-fix && cd rules_haskell_tests && bazel run //buildifier:buildifier-fix" <first commit>

where <first commit> is the first commit in your pull request. This fixes formatting for each of your commits separately, to keep the history clean.

You have to find a new git commit where all our shell.nix dependencies are available from the official NixOS Hydra binary cache.

At least for x86-linux this is guaranteed for the unstable channels. You can find the nixpkgs git commit of current unstable here:

https://nixos.org/channels/nixos-unstable/git-revision

That might be too old for your use-case (because all tests have to pass for that channel to be updated), so as a fallback there is:

https://nixos.org/channels/nixos-unstable-small/git-revision

You copy that hash to url in ./nixpkgs/default.nix. Don’t forget to change the sha256 or it will use the old version. Please update the date comment to the date of the nixpkgs commit you are pinning to.

GitHub Actions Cache

The GitHub actions CI pipeline uses actions/cache to store the Bazel repository cache. The cache-version must be updated manually in the env section in the workflow to invalidate the cache if any cacheable external dependencies are changed.

“unable to start any build”

error: unable to start any build; either increase '--max-jobs' or enable remote builds

We set --builders "" and --max-jobs 0 on CI to be sure all dependencies are coming from binary caches. You might need to add an exception (TODO: where to add exception) or switch to a different nixpkgs pin.

rules_haskell's People

Contributors

aherrmann avatar aherrmann-da avatar avdv avatar cgrindel avatar dependabot[bot] avatar erickj avatar facundominguez avatar fricklerhandwerk avatar fuuzetsu avatar googleson78 avatar guibou avatar guillaumegen avatar jcpetruzza avatar judah avatar lunaris avatar matsubara0507 avatar mboes avatar mergify[bot] avatar mrkkrp avatar nmattia avatar philderbeast avatar picnoir avatar profpatsch avatar renovate-bot avatar renovate[bot] avatar sebastiankg avatar smelc avatar thufschmitt avatar xophmeister avatar ylecornec avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rules_haskell's Issues

Add transitive JARs of Java dependencies to CLASSPATH

#80 introduced some interop with Java rules: if a Java provider is present, we add its jar to CLASSPATH during compilation. The right thing to do there however is to add transitive jars. This is blocked on an upstream bazel ticket (bazelbuild/bazel#4384).

Until then, the user has to manually insert all the jars they require even if they are already present somewhere down the Java compilation chain.

Import toolchain from Nixpkgs

Bazel has a notion of "toolchain", which are typically specified by giving a URL via one of new_http_archive, new_git_repository etc. It would be great if we could furthermore specify a toolchain as an attribute in Nixpkgs. Reasons:

  • It is wasteful to download a new toolchain, when this toolchain is likely already in the Nix store and can be shared across many Bazel repos.
  • There does not exist GHC bindists that, when unpacked, work on NixOS, which has ELF loaders and system libraries in non-standard locations. On NixOS, only the GHC provided by Nixpkgs works.
  • Nixpkgs can provide the strong reproducibility guarantees that Bazel requires. Given a Nixpkgs Git commit identifier, and an attribute name, the toolchain will always be byte-for-byte identical if it is downloaded now or in the future.

The problem is, I don't see how to achieve this with Bazel's current rule set. AFAIU, there is only a fixed set of primitive rules that are legal in a WORKSPACE file. What we'd want to define is a macro

new_nixpkgs_package(
  name = "somepkg",
  sha256 = "...",
  build_file = "BUILD.somepkg",
  nixpkgs_version = "...", # optional
)

and have that expand to some invocation of new_local_repository(path = somepkg_path, ...). But we can't hardcode such a path, which we don't know a priori. We can derive it after invoking some Nix CLI tools, however.

So what we'd need from Bazel is the ability to run arbitrary shell commands in a WORKSPACE file and allow fields of say new_local_repository to depend on the output of these commands. @jin any advice as to whether this is possible, or would make sense to add to the Bazel roadmap?

.hs-boot file support

We should have support for .hs-boot files (https://wiki.haskell.org/Mutually_recursive_modules). In current state this likely involves no real new work from us: just allowing these files in srcs should be enough. This ticket should only be closed after adding tests with .hs-boot files such that any future changes that are likely to break them (such as incremental builds) are caught and we don't lose the ability to build with these.

Relocatable binaries

Ideally rules_haskell should produce binaries that are relocateable. This is a discussion ticket. Most basic things to consider:

Relocateable where:

  • elsewhere on build host
  • onto a different host

There are multiple things to solve

  • non-absolute dynamic libraries: this is all .so objects we depend on that come from bazel itself for example.
  • absolute dynamic libraries: if we use something like rules_nixpkgs toolchain for system libs, we can only move the binary onto hosts with those libraries present
  • runtime files: bazel has some support for files needed at binary runtime: think of something like Cabal's getDataDir. These have to be moved with the binary.

Possibly we can use packaging rules to help somehow. I'd be interested to see what pkg_tar results in: perhaps it will dutifully copy both dependencies and runtime files with the binary out. I don't think we can do much about absolute dynamic binaries so if we want to cross host boundaries we'll have to do something magic. Ideally we probably don't want to rely on absolute paths at all. May require GHC work(?).


I think non-targets of this ticket are movable haskell_library and haskell_haddock artifacts: both may be desired though: haskell_library for general use and haskell_haddock if we want to host the docs somewhere for example.

Test non-Haskell dependencies in haskell_haddock

Create a haskell_haddock test like

haskell_haddock(
  deps = [":haskell-dep", ":non-haskell-dep"],
)

that verifies docs are still generated correctly. It should be Trivial™ as long as non-haskell-dep is marked as test-only.

Created from #47.

Generate BUILD files from Cabal descriptions

What

A command-line tool to generate BUILD files from Cabal package descriptions.

Would behave similarly to the gazelle tool provided by https://github.com/bazelbuild/rules_go

Why

Supporting dependencies

  • the vast majority of existing Haskell code uses Cabal
  • to use these projects as dependencies of a Bazel project requires they each have BUILD files
  • writing those files by hand is tedious & error-prone

Migrating to Bazel

  • People who want to start using Bazel will have a much easier time of it if there is a tool to help them migrate

Maintaining Haskell Bazel projects

  • Bazel works best with fine-grained dependencies—more opportunity for parallelism, less to rebuild when things change
  • It's very common to change the dependencies of a module when writing code
  • It would be great if you could just run a tool to update the BUILD file when this happens

Complicated dependencies

  • A lot of Haskell code depends on C libraries, which could theoretically be built in Bazel
  • Rather than many human beings re-learning how to do this, how about we encode the knowledge in a computer program?

The exact details & design of this tool need to be fleshed out. I would suggest closing this ticket once there's a minimal tool that handles the common 80% use case and filing follow-ups for the rest.

Check which outputs need to be in `haskell_haddock` outputs.

Currently haskell_haddock outputs all Haddock outputs that are in its direct deps results. This means that if we have

haskell_library(X)
haskell_library(Y, deps = X)
haskell_haddock(deps = Y)

outputs shown to the user are only those files from Y. Possibly we might need files from X too: if they aren't included then cross-package links might not work for example. Check if this is the case. This might be the same as #50.

Created from #47.

Merge jml/rules_haskell

This isn’t really an issue, just a way to kick off communications. (Do you have a mailing list I should be using instead?)

Very excited to discover this repo. I spent a chunk of time making https://github.com/jml/rules_haskell, but had to pause 3 months ago due to workload.

When I paused, I was working on generating rules files from cabal files, in a fashion not dissimilar to gazelle (see rules_go). I’m not sure if that’s pushed anywhere public yet.

  • I’m going to deprecate my repo in favour of this one
  • I’d appreciate it if you take a look at my repo, see if there are any ideas worth stealing, and steal them
  • If you don’t, I’ll try to find anything worth submitting
  • Do you feel that something to generate BUILD files (ala gazelle) is in scope for this project? If so,

Measure effect of not cleaning up linker flags

Since 0bba4b8 I have a feeling my linking has been slower. Perhaps we really do want to clean these up before passing them to GHC: if library is mentioned multiple times, do we pay multiple times for it?

However I may be imagining it so this ticket is about measuring the impact on linking itself.

Documentation (haddock) support.

Support builds with documentation.

As we may want to build all documentation, #29 might be related here. Possibly due to the way haddock works, bazel aspects are actually relevant here.

Using short_path has broken external imports

Recently merged f580110 has broken a usecase like this:

haskell-project-1:
  WORKSPACE
  BUILD
  …
haskell-project-2:
  WORKSPACE
  …
haskell-project-2.WORKSPACE:
  local_repository(name = "haskell_project_1", path = "/path/to/haskell-project-1")

haskell-project-2.BUILD:

haskell_library(
  name = "project-2",
  deps = [ "@haskell_project_1" ],
	File "/home/shana/.cache/bazel/_bazel_shana/73d4161e7228d1e130e8631a000a62c8/external/io_tweag_rules_haskell/haskell/path_utils.bzl", line 26, in path_to_module_path
		paths.relativize(hs_file.short_path, pkg_dir)
	File "/home/shana/.cache/bazel/_bazel_shana/73d4161e7228d1e130e8631a000a62c8/external/bazel_skylib/lib/paths.bzl", line 180, in paths.relativize
		fail("Cannot relativize paths above t...")
Cannot relativize paths above the current (unknown) directory

It works when path is used.

Use bazel's toolchain support

In #27 it was pointed out that bazel has toolchain support. We should use it and provide some default toolchains. This could also solve things like #24 for us as version information could be included out of the box.

Use skylib's _uniq over depset(foo).to_list()

We uses depset(foo).to_list() in many places to get unique elements of foo. Skylib offers this in a much faster way: build a dictionary with None values then use .keys(): apparently depsets do not support fast membership check and to_list() is definitely bad.

Minor ticket but simple.

Merge deps and prebuilt_dependencies

We have a problem in that to build anything non-trivial, we need prebuilt_dependencies: this is a list of packages that we expect to be available to GHC. It is up to the user to make sure a toolchain where they are present is used.

Sadly this causes a big inconvenience: the user has to both list everything in prebuilt_dependencies and then manually aggregate all these lists for all packages that toolchain will build back to toolchain definition. This is extremely tedious and goes out of date very quickly. We would like to only list packages in a single place: the dependencies for the package.

There is a way to achieve this and that is to stop using prebuilt_dependencies and instead pass honest bazel targets to deps. The question is where to get these targets. For purpose of this ticket, I will assume nix is still the answer. Note however there is no dependency on rules_nixpkgs from rules_haskell: in theory the user will be able to use the same mechanism with a different backend. It's up to the user.

Instead of generating a toolchain using ghcWithPackages (p: [ every-package-we-need-ever ]), generate package sets. A good candidate for this are LTS packages (but note user is not limited to these: they can cabal2nix/stackage2nix anything they want and use it just the same). The user defines:

http_archive(
  name = "lts",
  repository = …,
  strip_prefix = "lts-collection/lts-10.0",
)

Inside lts-10.0 there's a BUILD file with one bazel target per one package in the snapshot. These targets are provided by lts-10.0/WORKSPACE and may look a little like

nixpkgs_package(
  name = "aeson",
  attribute = "haskellPackages.aeson",
  build_file_content = """
filegroup(
  name = "haskell-outputs",
  …
  srcs = ..., # package db, package conf, libraries, everything needed.
)
"""
)

then inside build file we just have

something_todo(
  name = "aeson",
  target = "@aeson//:haskell-outputs",
)

Finally the user can now say

haskell_library(
  name = "my_lib",
  …,
  deps = [
    "@lts//:aeson"
  ],
)

Where rules_haskell comes in is to make sure it can deal with these dependencies properly: notably that it can find, use and include all necessary files. This for most part will involve -package-db and -package flags into outputs of those packages. Possibly something_todo above can just output a Provider that directly gives us all this information regardless of backend: we can provide default implementation for nix packages somewhere.

We now no longer specify Haskell packages in a toolchain: we unify these on bazel side. This allows us to only specify dependencies where they are needed and removes the tedium.

The reason this works is that bazel targets are lazy: nix (or other backend) will not build the targets until we try to use them. We can therefore safely generate full sets of LTS packages and just pick and choose without inconveniencing users who just need a couple of packages.

What we need is a tool to generate WORKSPACE/BUILD files for LTS (should be simple by re-using stackage2nix), consider if we want to host nix-backend pregenerated package sets in a repository somewhere (I would say yes: requiring that user runs stackage2nix on full LTS is too much: we can run it for every LTS and update periodically + provide instructions how to DYI if users want updates faster; automation of this is possible; having said that there's a new nightly every day so maybe it's not a good idea after all…).

We also need a way on rules_haskell side to unify these packages. We need to implement something_todo for nix backend which provide needed files.

In general I don't think there's that much work though I can imagine quite a few hurdles along the way. For the start we can of course just generate one LTS and use that as POC.

How to compile "vector"?

I'm trying to use https://hackage.haskell.org/package/vector, and I got this BUILD file:

load("@io_tweag_rules_haskell//haskell:haskell.bzl", "haskell_cc_import", "haskell_library")

cc_library(
    name = "empty",
    hdrs = ["include/vector.h"],
)

haskell_cc_import(
    name = "cbits",
    hdrs = [
        "include/vector.h",
        "internal/unbox-tuple-instances",
    ],
    shared_library = ":empty",
)

haskell_library(
    name = "vector",
    srcs = glob(["Data/**/*.*hs"]),
    prebuilt_dependencies = [
        "base",
    ],
    visibility = ["//visibility:public"],
    deps = [
        ":cbits",
    ],
)

This fails, because haskell_cc_import requires ".h", and anyway it's wrong, because vector.h is included in .hs files, it has nothing to do with C, but I can't list .h files as srcs for haskell_library, since it only accepts .hs and .hsc. unbox-tuple-instances is also a haskell file that's #included in .hs files. How can I get this to work?

`haskell_haddock` should re-use dependencies' GHC flags

Currently haskell_haddock provides its own flags to GHC: this is not a complete behaviour. If a package requires certain flags to compile, they have to be passed in through Haddock as well: after all, Haddock basically compiles the sources. It's not viable and it's very error prone to try to reimplement whole build logic we already have inside haskell_haddock. Instead we should capture any relevant flags during the build of the package itself and store the flags for later inspection. This way haskell_haddock should be able to just use these as well without having to re-implement the whole logic that went into generating them. How this should be done is an open question.

Created from #47. Also see comments there.

`haskell_haddock` has assumptions about haddock version

Different Haddock versions behave differently: in modern Haddock, haddock-utils.js is not created for example. This means that haskell_haddock can break easily if an unexpected version is used.

We should be able to specify/discover Haddock version then perform conditional tasks in haskell_haddock based on it.

Created from #47.

`haskell_haddock` should use a toolchain constraint

Currently we use the stock io_tweag_rules_haskell//haskell:toolchain. This only checks for presence of ghc, ghc-pkg and hsc2hs. This is fine but is not guaranteed to have Haddock present. Adding a check for haddock there is not correct either: if the user is not using haskell_haddock, they shouldn't have to provide it.

What we should do instead is use toolchain constraints: we could have a constraint for haddock (and ghc/ghc-pkg/hsc2hs too) and let bazel pick a toolchain that meets the constraints and inform the user.

This will involve adding the constraint to our existing toolchain too: it does provide Haddock after all.

Created from #47.

Support hsc2hs preprocessor

Not all source files can be handed over to as-is to GHC. hsc2hs is one such preprocessor that needs to be called by hand. Cabal in fact defines a number of such hard-coded preprocessors: happy and alex among them. I'm not too worried about those, which are easy for the user to special-case using a genrule. But hsc2hs is a tad tricky.

It sometimes creates multiple output files, sometimes not. At most, these output files are (given an input file A.hsc): A.hs, A_hsc.h, A_hsc.c. Other complications include the fact that the same -I and possibly even -L and -l compiler switches than for ghc need to be passed to hsc2hs as well.

We need a rule of the following form:

haskell_hsc(
  name = "foo",
  src = "Foo.hsc",
)

Check that declared version in haskell_toolchain matches

The user supplies a version number to haskell_toolchain. The user also provides a label locating a GHC install. We should check (at runtime) that the supplied GHC matches the expected version. For more user-friendly errors in case of a mismatch.

c_compile.bzl should use common mechanism from flags with rest of build rules

We have logic generating flags in c_compile.bzl which is also implemented in toolchain.bzl: for example -package flags. Split out common parts from compilation_defaults and re-use: re-implementing this everywhere is unfun.

Note this is unlike #57 as we're not trying to recover any flags after the fact, we just want to have a common place where they are generated and use that.

Document function return values

We're decent at documenting what the arguments to functions mean, but we nearly never document their return value when not None. This is flagged as a warning by Skylint.

Merge c_sources into srcs

Like in #26, where we moved .hsc files to the srcs, we should also move .c files there. Same reasoning: these files are source files like any other, passed as arguments to the same compiler (GHC).

Don't use HaskellPackageInfo as build information structure

HaskellPackageInfo contains far too much information and a lot of it is only needed during build: if a user was to inspect this provider for a target, they would find all information clumped together without being any wiser about the target itself.

gather_dependency_info should use a dedicated structure for build information and HaskellPackageInfo should only carry the information that it needs for itself + any other info that's absolutely needed where transitive dependencies are concerned.

Test expected failures

As suggested on #85 we should have tests that tests that certain configurations fail. For example that users has to explicitly state direct dependencies &c.

Dynamic libs need GHC version suffix

Dyn libs in dynamic-library-dirs section of ghc package files need to have -ghcVERSION name suffix. This is a problem because we don't know the version and we can't generate it dynamically which means we need to provide it as input to the rule. For now I'm just doing a ghcVersion attribute with default value but we need to figure out how to define and use toolchain info instead.

Support using official GHC bindists

#27 allows users to pull in their own tools to use during build. This lets them use GHC with prebuilt packages being available &c. The default there however uses rules_nixpkgs: in general we do not want to force nix on user.

We should provide basic targets for various GHC versions that will use official binary distribution. This will be plenty for users which only need vanilla GHC tooling to start building their project.

Likely related to #24

Resolve dynlib directory warnings

Getting a lot of warnings like the following in tests:

hsc-lib-1.0.0: Warning: dynamic-library-dirs: /home/mboes/.cache/bazel/_bazel_mboes/822aa44857e17f47d08bb198503f3032/bazel-sandbox/3199633755278556121/execroot/io_tweag_rules_haskell/bazel-out/k8-fastbuild/bin/tests/hsc/dynlib-hsc-lib-1.0.0 doesn't exist or isn't a directory

And these:

WARNING: cache does not exist: bazel-out/k8-fastbuild/bin/tests/library-deps/sublib/sublib-1.0.0/package.cache
ghc will fail to read this package db. Use 'ghc-pkg recache' to fix.

These need to be addressed.

cpphs/hsc are only usable from libraries

Most of development was focused with haskell_library in mind and haskell_binary is very poor right now. Factor out code there so that we get various phases for free. ccphs/hsc are example of such code which only works in haskell_library and doesn't fire in haskell_binary

See if there's a better heuristic for haskell_haddock doc locations than being neighbours

Currently in #47 we rely on documentation for various packages to be neighbours: the links are made to point to ../some-package. This happens to work but it seems to rely on an implementation detail: I don't know if this is guaranteed to be the case and won't just randomly break in some scenarios.

Figure out if there's' a better heuristic we could use. Absolute paths are not really an option as bazel moves things around. I think the way it is now might be OK.

Start an appropriate ghci from Bazel

Cabal and stack have a ghci subcommand (e.g. stack ghci) which loads the appropriate environment (compiler, dependencies, runs within docker or nix when appropriate) as well as the files for the project.

This is used by most IDEs (intero, dante, ghcid) to provide feedback to the programmer (type errors, type informations, completion options, etc…).

In order to provide compatibility with these IDEs, there should be an equivalent invocation of ghci with Bazel and rules_haskell, which uses the WORKSPACE and BUILD.

Core libraries visible even when dependency not declared

Minimal repro:

import Language.Haskell.TH

main = return ()

with following BUILD file:

haskell_binary(
  name = "binary-with-prebuilt",
  srcs = ["Main.hs"],
  prebuilt_dependencies = ["base"],
)

Expected a build failure, because "template-haskell" not in deps, but the build succeeds.

Benchmark support

We may want some benchmark support. Some ideas:

  • benchmarks that produce HTML/json/whatever: think criterion outputs
  • it could just be a haskell_binary with some bazel tag we pick that identifies it as a benchmark: at the very least the user can ask bazel to list/run all rules with the tag then.

I think we definitely should just start with haskell_binary alias with a rule tag.

'package.cache' conflict with multiple haskell_library deps

Take the following example BUILD file from https://github.com/jin/bazel_haskell_example:

haskell_binary(
    name = "main",
    srcs = ["Main.hs"],
    deps = [
	":a",
	":b",
    ],
)

haskell_library(
    name = "a",
    srcs = ["A.hs"],
)


haskell_library(
    name = "b",
    srcs = ["B.hs"],
)

This throws an error:

→ bazel build :main
ERROR: /Users/jin/Code/rules_haskell_conflict_error/BUILD:18:1: generated file 'package.cache' in rule 'b' conflicts with existing generated file from rule 'a', defined at /Users/jin/Code/rules_haskell_conflict_error/BUILD:12:1.
ERROR: error loading package '': Package '' contains errors.
INFO: Elapsed time: 0.051s

Interop with java rules

If a Java dependency is provided, add its (transitive?) jar(s?) to CLASSPATH during compilation. This should allow much more natural implementation of things like tweag/sparkle#133

Merge hscs and srcs

Some source files need preprocessing before being handed over to GHC. One way or another, they are still source files and the good news is that which preprocessor to run is always clear from the name of the source file alone. So we can list them in srcs along with the rest and add an extra action for those files that end in .hsc. Easier for the users: sources in srcs, dependencies in deps, etc.

Support source file hirerarchy that doesn't start at top level.

Frequently libraries live under src or similar. As we are guessing module names from file paths, we can only translate <project-root>A/B/C.hs right now. The way this is solved in cabal is simple: hs-source-dirs. Instead of using srcs as a flat list of files, we should take (directory, files) pairs instead. This way we can drop the directory prefix when needed.

Some alternative UI choices are possible.

Consider not using `ar` from `cpp` fragments.

We now use ctx.host_fragments.cpp.ar_executable to get ar. We may want to consider making the user pass ar as part of toolchain instead: this way we always ensure it's available and that it's the version user expects.

However ar is usually available/doesn't change so it's low priority.

ghcVersion should be derived auto-magically

We need this attribute to know what library name GHC will be trying to look for during linking. Currently the user has to specify it and it's error prone: even on master, this is set to 8.2.2 while CI uses GHC 8.0.2: this will give the user non-obvious error messages the moment they want to use shared library dependencies.

haskell_haddock support for (hyperlinked) sources

We should be able to generate sources and source links, preferably hyperlinked.

This shouldn't be too hard: we do need to replicate source file hierarchy but we already are able to do this. We may need extra tool in toolchain (#55) from what I remember.

haskell_haddock compatibility with prebuilt_dependencies

We may want to support prebuilt_dependencies of packages in haskell_haddock. This involves:

  • finding out paths of prebuilt_dependencies
  • adding them to --read-interface

We may be able to get these paths from ghc-pkg. To feed these paths into haddock itself we may want to use run_shell and interpolate or generate a response file with all the args prepopulated (but still needs run_shell most likely). These things can be a bit more granular by abusing files: for every prebuilt dependency, run ghc-pkg and produce a file with the path needed. Then when invoking haddock use $(cat path_of_prebuilt_dep_FOO) $(cat path_of_prebuilt_dep_BAR). This allows us to only invoke ghc-pkg once per every prebuilt dependency across all rules rather than call ghc-pkg for every single prebuilt dep for every single dep in the package which would likely be rather slow.

Before anything, think about whether we actually want this as a feature. prebuilt_dependencies is somewhat of a hack to begin with and maybe we should be encouraging to package more things and use prebuilt_dependencies less rather than encouraging its use.

Created from #47.

external_libraries requires absolute paths on OS

These paths are used in shared object libraries. We could get the needed libs from bazel in case of something like inline-java but it's unusable: best we can do is a relative path to external and during builds, the shared library moves around: the relative path becomes useless.

I don't know how to sanely solve this. For now I have something like

  external_libraries = {
    "jvm": "/nix/store/f4vdrkqqh71wffq3zz10ymywg3x28acw-openjdk-8u152b16/lib/openjdk/jre/lib/amd64/server",
…
}

but it's not really a solution…

.lhs file support

ghc accepts literate Haskell files. So should these rules. I can't build MissingH because of it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.