Apple Silicon과 Stack

Eunmin Kim·2022년 6월 7일
2

하스켈 일기

목록 보기
7/12
post-thumbnail

오늘은 하스켈 개발 환경 세팅에 관련해서 기록해보자. 지금 업무용으로 사용하고 있는 랩탑은 Apple Macbook Pro(M1 Pro)이다. 올 초만 해도 하스켈 생태계에서 M1을 잘 지원하지 않아 조금 고생했지만 지금은 자주 쓰는 툴은 모두 M1 버전을 지원해서 크게 문제는 없다. 다만 아직 하스켈 Docker 공식 이미지에는 몇 가지 문제로 linux/arm64/v8 아키텍처에서 stack이 설치되어 있지 않다. 하지만 cabal은 설치되어 있기 때문에 만약 같은 Dockerfile로 linux/arm64/v8linux/amd64에서 모두 빌드하는 것이 문제없다. stack 프로젝트라면 stack 프로젝트를 cabal로 바꿔주는 stack2cabal 바이너리로 stack.yamlcabal 프로젝트로 바꿔주면 된다.
하지만 우리 프로젝트는 Github Action CI에서 haskell/actions/setup@v2를 설치하고 stack으로 빌드해 실행 바이너리를 Docker로 복사하는 방식으로 배포하고 있어 지금은 특별히 문제가 되지 않는다.
아무튼 M1 맥북에서 stack을 사용하는 두 가지 방법에 대해 적어보려고 한다. 먼저 가장 깔끔한 방법은 brew install haskell-stack으로 설치하는 방법이다. 필요한 의존성과 몇 가지 문제를 해결한 버전이기 때문에 가장 쉽게 쓸 수 있다.
두 번째 방법은 GHCup으로 설치하는 방법이다. GHCup은 하스켈 개발할 때 필요한 GHC, Stack, HLS(Haskell Language Server), Cabal을 쉽게 설치하고 버전 관리할 수 있는 툴이기 때문에 유용하다. 최근 VSCode Haskell 확장도 GHCUp에서 설치한 HLS를 쉽게 사용할 수 있도록 통합이 되어 여러 가지로 GHCup을 쓰는 것이 좋다.
GHCup으로 설치한 현재 Stack 최신 버전은 몇 가지 차이점이 있는 것을 알았다. 특히 hpack 버전이 달랐다. GHCup으로 설치한 Stack-2.7.5 버전에는 hpack이 0.34.4 버전이고 Brew로 설치한 Stack-2.7.5 버전은 hpack이 0.34.6이다. hpack 버전이 다르면 협업을 할 때 조금 귀찮은 점이 있다. 상위 버전에서 생성된 cabal 파일을 하위 버전 hpack에서 고치려고 할 때 This file has been generated from package.yaml by hpack version 0.34.6.라는 에러와 함께 package.yaml을 바꿔도 cabal 파일이 바뀌지 않는다. 그래서 우리 팀은 모두 GHCup으로 설치한 Stack을 사용하기로 했다.
앞에서 GHCup으로 설치한 Stack에 살짝 문제가 있다고 했는데 터미널에서 stack build를 할 때 Segmentation Fault가 발생하는 문제가 있다. 이 문제는 llvm 의존성 문제로 알려져 있는데 Brew에서 설치한 버전은 llvm@12 의존성을 설치하고 이 버전을 사용하도록 LDFLAGS, CPPFLAGS가 빌드할 때 설정되는 것 같다. GHCup으로 설치한 버전(현재 단순히 미리 빌드된https://downloads.haskell.org/ghcup/unofficial-bindists/stack/2.7.5/stack-2.7.5-osx-aarch64.tar.gz 파일을 받아서 적절한 위치에 압축을 풀어주는 것이 전부이다)은 따로 llvm@12 버전의 공유 라이브러리를 사용하도록 설정해줘야 하는 번거로움이 있다. Segmentation Fault가 발생하지 않는 경우도 있는 것 같은데 아마 로컬 llvm@12 버전과 관련 있는 것 같다. 현재 나의 llvm 버전은 13인 것 같다.

$ llvm-gcc --version  
Apple clang version 13.1.6 (clang-1316.0.21.2)
Target: arm64-apple-darwin21.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

그래서 brew install llvm@12를 따로 설치하고 GHCup에서 설치한 stack 파일을 실행할 때 LDFLAGSCPPFLAGSllvm@12로 설정하는 스크립트로 감싸서 stack을 사용하고 있다.

#!/bin/bash

export PATH="/opt/homebrew/opt/llvm@12/bin:$PATH"
export LDFLAGS="-L/opt/homebrew/opt/llvm@12/lib"
export CPPFLAGS="-I/opt/homebrew/opt/llvm@12/include"

~/.ghcup/bin/stack "$@"

이렇게 사용하면 Segmentation Fault 문제없이 잘 사용할 수 있다. 그래도 어서 이 문제가 해결되어 새로 설정하는 사람들이 쉽게 M1에서 stack을 사용할 수 있으면 좋겠다.

profile
Functional Programmer @Constacts, Inc.

5개의 댓글

comment-user-thumbnail
2022년 6월 7일

맥에서 여러 언어의 개발환경을 관리하기 위해 asdf를 유용하게 사용하는데, 하스켈용 플러그인도 있더군요. 이것도 도움이 될 수 있어보입니다.

https://github.com/asdf-community/asdf-haskell

1개의 답글
comment-user-thumbnail
2022년 6월 16일

hpack 버전이 다르면 협업을 할 때 조금 귀찮은 점이 있다. 상위 버전에서 생성된 cabal 파일을 하위 버전 hpack에서 고치려고 할 때 This file has been generated from package.yaml by hpack version 0.34.6.라는 에러와 함께 package.yaml을 바꿔도 cabal 파일이 바뀌지 않는다.

협업 시에 모두 stack을 쓰는 환경이라면 .cabal file을 version manager에 올리지 않는 방법도 있습니다. 그러면 0.34.6든 0.34.5든 자기가 만든 .cabal을 쓸테고, 굳이 따지자면 저 둘의 호환성 문제만 남게 되겠죠.

1개의 답글
comment-user-thumbnail
2022년 7월 3일

김은민님을 여기서 뵙네요
얼마전에 쏙쏙 들어오는 함수형 읽어봤는데 너무 재밌게 읽었습니다.
번거로우시겠지만... 앞으로도 많은 함수형 서적들 번역 부탁드립니다 (특히 하스켈!!!)
멀리서 응원하겠습니다!

답글 달기