Skip to content
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

haskell-stack: bootstrap stack with stack #1480

Closed
wants to merge 1 commit into from

Conversation

ilovezfs
Copy link
Contributor

@ilovezfs ilovezfs commented May 27, 2016

The idea for this commit is from Emanuel Borsboom, who has suggested that bootstrapping should help prevent Homebrew-specific regressions caused by Cabal's Solver choosing different versions of stack's
dependencies as compared to the versions used when building the upstream binaries.

Note that the stage 1 stack gathers its own resources including even the compiler. Regarding the security implications of this approach, see
https://www.fpcomplete.com/blog/2015/07/package-security-in-stack
https://www.fpcomplete.com/blog/2016/05/stack-security-gnupg-keys

A non-default option to skip bootstrapping and just install the stage 1 build is included as well.

@ilovezfs
Copy link
Contributor Author

Also, see commercialhaskell/stack#2182 and commercialhaskell/stack#2192 for the prior conversations.

@ilovezfs
Copy link
Contributor Author

CC @zmwangx

@ilovezfs ilovezfs added the haskell Haskell use is a significant feature of the PR or issue label May 27, 2016
@zmwangx
Copy link
Contributor

zmwangx commented May 27, 2016

system "stack", "--local-bin-path=#{bin}", "install"

works only because stack installs a single fully-linked executable and no more. I wonder if this is future-proof.

I'm 👍 on this if it provably solves a problem though.

@borsboom
Copy link
Contributor

We (the Stack release team) very much intend to keep stack as a single executable as ease-of-distribution is a major goal. Our new version release process also includes testing and sending a PR to homebrew-core, so we'll definitely notice if it stops working.

I'm building this PR now, so I'll let you know whether it solves the original problem.

@ilovezfs
Copy link
Contributor Author

@borsboom If you don't mind, please also check the current bottle that's distributed in master as well since it differs from the one the user reporting the original issue was using, so we don't actually know at this point whether the current bottle is or isn't broken in the relevant sense.

@borsboom
Copy link
Contributor

borsboom commented May 27, 2016

Ok, I just compared the existing bottle to the new build, and I can confirm that the existing bottle fails and the new build succeeds:

$ ~/fpco/tmp/stack-1.1.2-homebrew-old --docker exec echo hello
SQLite3 returned ErrorCan'tOpen while attempting to perform open "/Users/manny/.stack/docker.db".

$ ~/fpco/tmp/stack-1.1.2-homebrew-new --docker exec echo hello
hello

So this solves the original problem and I expect it will prevent other problems in the future. Incidentally, other Haskell-built programs may also benefit from being built by Stack rather than cabal-install if their source code includes a stack.yaml, for the same reason: stack is all about reproducible builds.

@ilovezfs
Copy link
Contributor Author

ilovezfs commented May 27, 2016

@borsboom That's with

sha256 "29f4a14de6d367bcf8aaca3baa39a325566d093974dd3175c5347c79b3ee3407" => :el_capitan
?

@borsboom
Copy link
Contributor

Yes, my local Formula/haskell-stack.rb has the same SHA256 for the bottle and I just installed that bottle before running my test.

@ilovezfs
Copy link
Contributor Author

@borsboom Homebrew/legacy-homebrew#49158 for the prior conversation on whether using stack more generally might be a viable option for Homebrew.

@ilovezfs
Copy link
Contributor Author

@borsboom In a local test, not the CI for the PR which as you can see passed, I just got this:

install: mkdir /private/tmp/haskell-stack-20160527-86482-aj8hzg/stack-1.1.2/stack_root/programs/x86_64-osx/ghc-7.10.3/lib: File exists
make[1]: *** [install_libffi_headers] Error 71
make[1]: *** Waiting for unfinished jobs....
make: *** [install] Error 2

/usr/local/Library/Homebrew/debrew.rb:11:in `raise'
BuildError: Failed executing: stack setup

Do I need to deparallelize stack setup in some way? Is that a known bug?

@borsboom
Copy link
Contributor

stack setup doesn't currently expect to be run in parallel in the same STACK_ROOT. So either de-parallelizing, or making sure concurrent runs go in different STACK_ROOTs should fix that.

@ilovezfs
Copy link
Contributor Author

@borsboom does stack install have the same issue?

@borsboom
Copy link
Contributor

It potentially can if parallel installs of the same package go into the same shared sandbox (in the STACK_ROOT).

@ilovezfs ilovezfs force-pushed the haskell-stack-bootstrap branch 2 times, most recently from aa9b535 to ebe03b6 Compare May 27, 2016 17:22
@ilovezfs
Copy link
Contributor Author

@borsboom PR refreshed. I think that will fix it.

@ilovezfs ilovezfs added the maintainer feedback Additional maintainers' opinions may be needed label May 27, 2016
@ilovezfs
Copy link
Contributor Author

@DomT4 I wonder if it would be wise to specify a folder within HOMEBREW_CACHE for the stack downloads to accumulate, on analogy to the java cache thingy.

cabal_sandbox do
cabal_install

# Let `stack` handle its own parallelization
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens without this? Seems weird.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it work to just have it for stack setup and leave stack install as normal? No worries if not, just seems odd.

Copy link
Contributor Author

@ilovezfs ilovezfs May 27, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible that the only problem was that I was explicitly setting STACK_ROOT. I should try with just passing -j#{jobs} but not wrapped in the ENV.deparallelize.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we may not need to pass -j#{jobs} at all either, but it seems that's probably the "right" thing to do, as it's the same thing we do with cabal: https://github.com/Homebrew/brew/blob/master/Library/Homebrew/language/haskell.rb#L58

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the breakage occurred without setting -j at all, but with setting STACK_ROOT.

@ilovezfs ilovezfs force-pushed the haskell-stack-bootstrap branch from ebe03b6 to 8dc2c08 Compare May 27, 2016 19:20
@ilovezfs
Copy link
Contributor Author

ilovezfs commented May 28, 2016

Without the wrapping in ENV.deparallelize, the error showed up again during stack setup, this time on CI.

install: mkdir /private/tmp/haskell-stack-20160527-31238-lzl7mu/stack-1.1.2/.stack/programs/x86_64-osx/ghc-7.10.3/lib: File exists
make[1]: *** [install_includes] Error 71
make[1]: *** Waiting for unfinished jobs....
make: *** [install] Error 2

So I've put that back in.

@ilovezfs ilovezfs force-pushed the haskell-stack-bootstrap branch from 8dc2c08 to 4c32736 Compare May 28, 2016 15:21
@ilovezfs
Copy link
Contributor Author

On the one hand, I'm pleased that this bootstrapping technique works without much fuss. On the other hand, I kind of wish we knew what was causing SQLite3 returned ErrorCan'tOpen while attempting to perform open "/Users/manny/.stack/docker.db". and could address that directly.

@DomT4
Copy link
Contributor

DomT4 commented May 28, 2016

@ilovezfs
Copy link
Contributor Author

I'm also thinking this formula should be renamed stack because I cannot seem to stop myself from typing brew install stack, which seems to be impervious to :ilovezfs-self-fix.

@borsboom
Copy link
Contributor

On the one hand, I'm pleased that this bootstrapping technique works without much fuss. On the other hand, I kind of wish we knew what was causing SQLite3 returned ErrorCan'tOpen while attempting to perform open "/Users/manny/.stack/docker.db". and could address that directly.

I actually think of this bootstrapping technique fixing a general problem (that there's no guarantee that the Homebrew version of stack has been built the same way as other stack binaries), and it's just a side-effect that it fixes the SQLite error. As for the original cause of the SQLite error: IMO that's for the Stack team to determine, and has nothing to do with Homebrew. Bugs get introduced all the time by changing Haskell dependency versions and this is just the latest example, which is why we're kind of anal about wanting consistent, reproducible builds.

@ilovezfs
Copy link
Contributor Author

ilovezfs commented May 28, 2016

Sure, but this bug precedes the monkeying with (buildpath/"cabal.config").write("allow-newer: base,transformers\n"), so it implies there's a bug with https://github.com/commercialhaskell/stack/blob/master/stack.cabal which must be permitting Solver to find an invalid solution.

@borsboom
Copy link
Contributor

Not sure if I'm reading it correctly, but it looks like you're only using the sdist for the stack install part. The sdist is actually more important for the cabal install part because it has full pvp-bounds in stack.cabal (whereas the plain source code from Github does not), but there's no harm using it for the stack install part as well.

@ilovezfs ilovezfs force-pushed the haskell-stack-bootstrap branch from 7eed978 to d1e44e6 Compare May 30, 2016 13:33
@ilovezfs
Copy link
Contributor Author

@boorsboom OK, that makes sense. I did that because the patches here (and in the future) are much more likely to apply cleanly to the GitHub tag. Would it be correct to simply replace the cabal.config with the one from sdist?

@ilovezfs
Copy link
Contributor Author

ilovezfs commented May 30, 2016

I suppose just using sdist's for the stage 1 build will be problematic, though, because it will be missing https://github.com/commercialhaskell/stack/commit/f848ceb16b147636e112068d346267b1de661c1b.patch Nevermind.

@ilovezfs ilovezfs force-pushed the haskell-stack-bootstrap branch from d1e44e6 to 4e0818c Compare May 30, 2016 14:02
@ilovezfs
Copy link
Contributor Author

@borsboom OK, refreshed the PR using sdist's stack.cabal. How's that looking?

@ilovezfs ilovezfs force-pushed the haskell-stack-bootstrap branch from 4e0818c to 83f8da6 Compare May 30, 2016 15:27
@borsboom
Copy link
Contributor

I guess you're just copying the stack.cabal? I'd probably recommend just using the whole sdist verbatim for the sake of simplicity, but either way works so I don't feel really strongly about it.

@ilovezfs
Copy link
Contributor Author

@borsboom That's probably a good idea. My concern is just that if an important GitHub commit "foo.patch" is needed in the future between releases that someone might get confused when the commits refuse to apply cleanly, but I suppose we can cross that bridge if/when we get there.

The idea for this commit is from Emanuel Borsboom, who has suggested
that bootstrapping should help prevent Homebrew-specific regressions
caused by Cabal's Solver choosing different versions of stack's
dependencies as compared to the versions used when building the upstream
binaries.

Note that the stage 1 stack gathers its own resources including even the
compiler. Regarding the security implications of this approach, see
https://www.fpcomplete.com/blog/2015/07/package-security-in-stack
https://www.fpcomplete.com/blog/2016/05/stack-security-gnupg-keys

A non-default option to skip bootstrapping and just install the stage 1
build is included as well.
@ilovezfs ilovezfs force-pushed the haskell-stack-bootstrap branch from 83f8da6 to 4a31dd4 Compare May 30, 2016 15:43
@ilovezfs
Copy link
Contributor Author

@borsboom OK, updated to use sdist only. Note that the version needs to be specified manually since brew's version detector thingy is confused by the sdist url.

@borsboom
Copy link
Contributor

LGTM!

@ilovezfs
Copy link
Contributor Author

@borsboom Cool. BTW, I suspect this will also work out-of-the-box on Linuxbrew.

@ilovezfs
Copy link
Contributor Author

@borsboom Is there an environment variable for specifying a cache location for the downloads? I think we might be able to accomplish something similar to the ENV.java_cache feature we have.

@borsboom
Copy link
Contributor

Those are all kept in the STACK_ROOT.

@DomT4
Copy link
Contributor

DomT4 commented May 30, 2016

@ilovezfs Is haskell-stack used for other Homebrew formulae? If it isn't we don't "care" about how it behaves outside Homebrew's usage.

@ilovezfs
Copy link
Contributor Author

@DomT4 The current version here is downloading a ton of external resources every run... including the compiler, and then throwing it all out.

@ilovezfs
Copy link
Contributor Author

@DomT4 Hence #1480 (comment)

@DomT4
Copy link
Contributor

DomT4 commented May 30, 2016

@ilovezfs I see. Sorry, didn't see the original comment, this thread got kind of busy 😄. If usage is limited to this formula I don't have an objection to an ENV variable limited to this formula specifically.

@ilovezfs
Copy link
Contributor Author

ilovezfs commented May 30, 2016

@DomT4 Longer-term I think we're going to want to switch from the current cabal sandbox approach to some combination of haskell stack (the current blocker to that is commercialhaskell/stack#848) and cabal new-build, but at the moment the concern is only this formula and users with slow Internet connections and/or bandwidth caps, so I'm not imagining anything for superenv at this point, just a one-off analogue for this formula alone.

However, I'm not sure if there's any way to "reset" a STACK_ROOT but preserve the downloads to avoid "contamination" between builds.

@borsboom
Copy link
Contributor

You could probably delete everything except $STACK_ROOT/programs if you just want to keep GHC. Then you lose the package caching, though.

@DomT4
Copy link
Contributor

DomT4 commented May 30, 2016

@ilovezfs Cool. You're more than welcome to introduce a tweak for the single formula here as long as we avoid introducing DSL where the application is narrow, is basically what I was getting at vaguely. I'll trust you on longer-term Cabal changes, I've done my utmost to avoid getting involved in Haskell stuff heh.

@ilovezfs
Copy link
Contributor Author

I've done my utmost to avoid getting involved in Haskell stuff heh.

😆 😆

introducing DSL where the application is narrow

Wouldn't dream of it :)

@MikeMcQuaid
Copy link
Member

PR seems good to me 👍

@ilovezfs
Copy link
Contributor Author

Setting ENV["STACK_ROOT"] = "#{HOMEBREW_CACHE}/stack_root" at the top, once it's installed and the dust has settled:

iMac-TMP:~ joe$ du -sh ~/Library/Caches/Homebrew/stack_root/
1.8G    /Users/joe/Library/Caches/Homebrew/stack_root/

Worth exploring further perhaps, but I don't think that needs to block this PR.

@ilovezfs ilovezfs closed this in d9afc24 May 30, 2016
@ilovezfs
Copy link
Contributor Author

@borsboom It has 🚢'ed if you'd like to verify it's 💯 .

@ilovezfs ilovezfs deleted the haskell-stack-bootstrap branch June 3, 2016 10:28
@Homebrew Homebrew locked and limited conversation to collaborators May 4, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
haskell Haskell use is a significant feature of the PR or issue in progress Stale bot should stay away maintainer feedback Additional maintainers' opinions may be needed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants