Can I use Haskell's stack to compile and run _only_ the tests? - haskell

When I run a stack test or a stack test <package>:<test-suite> then the output looks something like:
package: configure (lib + exe + test)
package: build (lib + exe + test)
package: copy/register
package: test (suite: tests)
And it ends up basically compiling all my changes twice: once for the exe or lib and a second time for the test.
What I would like is a command like stack test --test-only that would produce something like:
package: configure (test)
package: build (test)
package: copy/register
package: test (suite: tests)
I have already looked through the available command line flags and stack documentation. I have also attempted a google search to see if anyone has talked about something similar.
So my questions are:
1. What is the best currently available way to compile and run only the tests? My best guess on this one is to try putting the tests in a separate cabal package.
2. Is there some reason why stack doesn't or couldn't do this?

I have made some experiments using stack build <pkgname>:test:<testsuite> and found nothing really pleasing when you have all your app/src/main in one directory cabal-project.
Now I did not investigate if this is an issue due to stack using Cabal as a library or whether this is a stack issue.
here are a few issues that might be related
Turning on test coverage rebuilds all packages #1940
stack ghci foo:test:bar uses library dependencies unless --test is passed #1845
Allow stack test to skip certain test suites #1659
How to specify a subset of tests to run? #2519
"stack test " does not run tests? #1961
But I guess you'd have to file a bug if noone provides a better answer.
A possible but quite ugly solution (in my opinion) is to split test-suite, app and library into separate cabal projects - here is the example folder structure I used for testing.
myproject
├── stackapp
│   ├── app
│   │   └── Main.hs
│   ├── ChangeLog.md
│   ├── LICENSE
│   ├── Setup.hs
│   └── stackapp.cabal
├── stacksrc
│   ├── ChangeLog.md
│   ├── LICENSE
│   ├── Setup.hs
│   ├── src
│   │   └── Lib.hs
│   └── stacksrc.cabal
├── stacktest
│   ├── ChangeLog.md
│   ├── LICENSE
│   ├── Setup.hs
│   ├── src
│   ├── stacktest.cabal
│   └── tst
│   └── Spec.hs
└── stack.yaml
stack.yaml
resolver: lts-7.3
packages:
- './stacksrc'
- './stacktest'
- './stackapp'
extra-deps: []
flags: {}
extra-package-dbs: []
Note that you need to include a "dummy" library section in order to make it compile, cabal is picky about cabal files that have neither lib nor exe part.
stacktest.cabal
...
library
-- dummy
build-depends: base >=4.9 && <4.10
hs-source-dirs: src
default-language: Haskell2010
test-suite tests
type: exitcode-stdio-1.0
main-is: Spec.hs
hs-source-dirs: tst
build-depends: base
, stacksrc
, hspec
, hspec-expectations-pretty-diff
default-language: Haskell2010
Then you can modify tests and run stack stacktests:test:tests without rebuilding the lib and/or the app part, but stack is intelligent enough designed to rebuild the lib part if you change it before you run the tests.
UPDATE
For future reference here is a link to the opened ticket:
To use stack to compile and run only the tests #2710

Related

Can I have a Cargo workspace with two binaries relying on a library where only one binary enables a feature of the library?

I have a Cargo workspaces project with the structure:
├── src
├── lib
│   └── Cargo.toml
├── tools
│   ├── src/bin/foo.rs
│   └── Cargo.toml
└── Cargo.toml
., lib and tools are my three workspace members. lib has a feature that enables tracing of the execution of a performance-sensitive function. As this feature adds performance overhead, the main binary should not enable it, but the foo binary is a testing tool which uses it.
However, Cargo workspaces take the union of the features of the two binaries, so the main binary ends up including the feature anyway.
How can I fix this?

Workspace optional dependencies (std & no_std)

I am developing a code for my school's rocketry team and I have two programs, one meant to flash the on-board computer and another to run some data analysis on the flight data. The chip code uses no_std while the data analysis program uses std. The data-analysis code will run on my PC, and the chip code will run on the chip.
Here is my workspace root Cargo.toml and my project graph:
[workspace]
members = [
"chip",
"data-analysis",
]
.
├── Cargo.lock
├── Cargo.toml
├── chip
│   ├── Cargo.toml
│   ├── memory.x
│   ├── openocd.cfg
│   ├── openocd.gdb
│   └── src
│   └── main.rs
├── data-analysis
│   ├── Cargo.toml
│   └── src
│   └── main.rs
├── README.md
└── resources
├── 3m.mkd
├── data-stm32f103c8t6.pdf
├── links.txt
├── reference-stm32f103xx.pdf
├── schematic-stm32f103c8t6.png
└── todo.txt
I have decided to use a workspace to organize my code. When I attempt to build the workspace, I get the error:
error[E0463]: can't find crate for `std`
|
= note: the `thumbv7m-none-eabi` target may not support the standard library
= note: `std` is required by `data_analysis` because it does not declare `#![no_std]`
= help: consider building the standard library from source with `cargo build -Zbuild-std`
When I compile with cargo build -Zbuild-std I get the error:
error[E0463]: can't find crate for `panic_abort`
error[E0658]: use of unstable library feature 'restricted_std'
|
= help: add `#![feature(restricted_std)]` to the crate attributes to enable
However I need no_std and not restricted_std.
I understand that dependencies for all files are stored in Cargo.lock and presumably that's why it is producing this error. My question is, how do I express to the compiler that I need std for data_analysis but not for chip code? Should I even be using workspaces over just using one package with multiple binaries and using [features] in the Cargo.toml?
It seems like you're trying to complile data_analysis with the same target as chip. I recommend just getting rid of the root Cargo.toml and compiling the respective ones in the folders. You could make a Makefile to automate this easily

Why is cargo build cache invalidating?

I have a barebones workspace project:
.
├── build-debug.sh
├── Cargo.lock
├── Cargo.toml
├── common
│   ├── Cargo.toml
│   └── src
│   └── lib.rs
├── rs-test.iml
├── server
│   ├── Cargo.toml
│   └── src
│   └── main.rs
└── wui
├── Cargo.toml
└── src
└── lib.rs
The rs files either empty or just an empty main function.
The server and the wui depends on common: common = { path = "../common" }.
The common project has one crates.io dependency with I suppose build script or proc macro dependency.
The build script:
cargo build -p wui --target wasm32-unknown-unknown
cargo build -p server
The problem:
When I rebuild the unchanged project, some wui dependencies are getting invalidated/rebuilt, then the same for server.
Either:
remove the wasm32 target flag
replace the dependency with a simple crate without build time compiled dependencies
It does not rebuild the subprojects anymore.
Is this a cargo bug? What can I do?
It's probably not a cargo bug. What is likely happening here is that your crates.io dependency (you don't mention what it is, which might have been useful) has different dependencies or features depending on the target architecture. Thus, as you alternate between building the WASM target and your host target, stuff is being rebuilt.
Perhaps it would be better in this case to stop using the Cargo workspace and build the server and wui separately; this way you'll have separate target directories for the server and wui, which takes some extra disk space and takes longer for non-incremental compilation, but will prevent you from having to rebuild that stuff all the time as you build both.

Can I swap out the library used by a binary with a wrapper when building a third-party crate?

Let's say there is a vendored third-party cargo project consisting of a library plem and a binary plem_main that I want to extend with some functionality of my own. Crucially, the functionality needs to go in the library plem, not the binary plem_main (which can stay the same). I could write a wrapper my_plem around the library that offers the same interface to the binary, but with the extra functionality included. The project would be set up like this:
.
├── Cargo.toml
├── my_plem
│   ├── Cargo.toml
│   └── src
│   └── lib.rs
└── third-party
├── plem
│   ├── Cargo.toml
│   └── src
│   └── lib.rs
└── plem_main
├── Cargo.toml
└── src
└── main.rs
my_plem/src/lib.rs would depend on things in third-party/plem/src/lib.rs and reexport or overwrite the functions exported by the latter. Is there a good way to get cargo to build the binary plem_main on top of my_plem instead of plem?
"Best" here means that the solution has no or minimal merge conflicts when updating plem in my project and doesn't duplicate the code of plem_main. Ideally it does not touch third-party at all.

Stack using binaries of other package

I have two projects in my user directory ~, the project A and B.
I run stack init and later stack build on the project A. Then, I have
the binaries of the A package in a folder ~/.stack-work/install/x86_64-linux/lts-6.0/7.10.3/bin. The issue is B needs this version of the binaries from A package, and then try the same build with stack on the B project directory. I tried on ~/B run the following command without success.
stack build ~/.stack-work/install/x86_64-linux/lts-6.0/7.10.3/bin
How can I do that? What if I create a third package C, and need something similar?
Excerpts:
The A.cabal content.
name: A
version: 1.1
And the B.cabal.
name: B
version: 1.0
build-depends: A>= 1.1
Then,
$ stack init
Looking for .cabal or package.yaml files to use to init the project.
Using cabal packages:
- B.cabal
Selecting the best among 8 snapshots...
* Partially matches lts-6.0
A version 1.0 found
- A requires ==1.1
This may be resolved by:
- Using '--omit-packages to exclude mismatching package(s).
- Using '--resolver' to specify a matching snapshot/resolver
But I actually have the version 1.1 of A build.
You don't need to include the project A's bin directory - that was a red herring.
Organize your files like this:
.
├── stack.yaml
├── project-A
│   ├── LICENSE.txt
│   ├── Setup.hs
│   ├── project-A.cabal
│   └── src
│   └── ...
│
└── project-B
   ├── Setup.hs
   ├── project-B.cabal
   └── src
   └── ...
Your top-level stack.yaml file will look like:
resolver: lts-5.13
packages:
- project-A/
- project-B/
Then in the top-level directory run stack build.
I'll take a stab at answering your question...
How about putting
~/.stack-work/install/x86_64-linux/lts-6.0/7.10.3/bin
in your PATH? If the other project really needs binaries (i.e. programs) built by another project, this would be the way to do it.
Or, copy the built programs to some directory in your current PATH - i.e. /usr/local/bin or ~/bin.
If this doesn't answer your question, please post the cabal files for both projects.
I found an answer after digging into the FAQ of stack. Create a file stack.yaml into B folder. At first the content could be:
resolver: lts-6.0
packages:
- '.'
- '/home/jonaprieto/A'
extra-deps: []
Then, it runs:
$ stack build

Resources