mirror of
https://github.com/haskell/haskell-ide-engine.git
synced 2024-09-11 11:05:30 +03:00
Merge upstream
This commit is contained in:
commit
f34aacacce
1
.azure/linux-cabal.bashrc
Normal file
1
.azure/linux-cabal.bashrc
Normal file
@ -0,0 +1 @@
|
||||
export PATH=$HOME/.cabal/bin:/opt/cabal/$CABAL_VERSION/bin:/opt/ghc/$GHC_VERSION/bin:$HOME/.local/bin:$PATH
|
76
.azure/linux-cabal.yml
Normal file
76
.azure/linux-cabal.yml
Normal file
@ -0,0 +1,76 @@
|
||||
jobs:
|
||||
- job: Linux_Cabal
|
||||
timeoutInMinutes: 0
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
strategy:
|
||||
matrix:
|
||||
ghc-8.6.5:
|
||||
GHC_VERSION: "8.6.5"
|
||||
ghc-8.4.4:
|
||||
GHC_VERSION: "8.4.4"
|
||||
variables:
|
||||
CABAL_VERSION: "3.0"
|
||||
steps:
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: '"cabal-store" | "$(Agent.OS)" | "$(CABAL_VERSION)" | "$(GHC_VERSION)" | $(Build.SourcesDirectory)/cabal.project | $(Build.SourcesDirectory)/haskell-ide-engine.cabal | $(Build.SourcesDirectory)/hie-plugin-api/hie-plugin-api.cabal'
|
||||
path: .azure-cache
|
||||
cacheHitVar: CACHE_RESTORED
|
||||
displayName: "Download cache"
|
||||
- bash: |
|
||||
mkdir -p $HOME/.cabal
|
||||
tar -vxzf .azure-cache/cabal-root.tar.gz -C /
|
||||
mkdir -p $HOME/.ghc
|
||||
tar -vxzf .azure-cache/ghc-root.tar.gz -C /
|
||||
mkdir -p dist-newtyle
|
||||
tar -vxzf .azure-cache/cabal-dist.tar.gz
|
||||
displayName: "Unpack cache"
|
||||
condition: eq(variables.CACHE_RESTORED, 'true')
|
||||
- bash: |
|
||||
git submodule sync
|
||||
git submodule update --init
|
||||
displayName: Sync submodules
|
||||
- bash: |
|
||||
source .azure/linux-cabal.bashrc
|
||||
cabal v2-update
|
||||
cabal v2-build --only-dependencies
|
||||
displayName: Build dependencies
|
||||
- bash: |
|
||||
source .azure/linux-cabal.bashrc
|
||||
cabal v2-build
|
||||
displayName: Build `hie`
|
||||
- bash: |
|
||||
source .azure/linux-cabal.bashrc
|
||||
cabal v2-install --overwrite-policy=always # `hie` binary required locally for tests
|
||||
displayName: Install `hie`
|
||||
- bash: |
|
||||
source .azure/linux-cabal.bashrc
|
||||
cabal v2-build --enable-tests --enable-benchmarks --only-dependencies
|
||||
displayName: Build Test-dependencies
|
||||
- bash: |
|
||||
sudo apt update
|
||||
sudo apt install z3
|
||||
displayName: "Install Runtime Test-Dependencies: z3"
|
||||
- bash: |
|
||||
source .azure/linux-cabal.bashrc
|
||||
# to not reinstall hie
|
||||
cd $(Agent.TempDirectory)
|
||||
cabal v2-install liquidhaskell-0.8.6.2 -w /opt/ghc/8.6.5/bin/ghc --overwrite-policy=always
|
||||
displayName: "Install Runtime Test-Dependencies: liquidhaskell"
|
||||
- bash: |
|
||||
source .azure/linux-cabal.bashrc
|
||||
cabal v2-build hoogle
|
||||
cabal v2-exec hoogle generate
|
||||
displayName: "Install Runtime Test-Dependencies: hoogle database"
|
||||
- bash: |
|
||||
source .azure/linux-cabal.bashrc
|
||||
# TODO: Investigate why the test suite can't be run in parallel
|
||||
cabal v2-test -j1
|
||||
displayName: Run Test
|
||||
- bash: |
|
||||
mkdir -p .azure-cache
|
||||
tar -vczf .azure-cache/cabal-root.tar.gz $HOME/.cabal
|
||||
tar -vczf .azure-cache/ghc-root.tar.gz $HOME/.ghc
|
||||
tar -vczf .azure-cache/cabal-dist.tar.gz dist-newstyle
|
||||
displayName: "Pack cache"
|
@ -3,10 +3,8 @@ jobs:
|
||||
timeoutInMinutes: 0
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
strategy:
|
||||
matrix:
|
||||
shake:
|
||||
YAML_FILE: install/shake.yaml
|
||||
variables:
|
||||
YAML_FILE: install/shake.yaml
|
||||
steps:
|
||||
- bash: |
|
||||
export STACK_ROOT="$(Build.SourcesDirectory)"/.stack-root
|
||||
@ -32,9 +30,5 @@ jobs:
|
||||
displayName: Run help of `install.hs`
|
||||
- bash: |
|
||||
source .azure/linux.bashrc
|
||||
stack install.hs stack-install-cabal
|
||||
displayName: Run stack-install-cabal target of `install.hs`
|
||||
- bash: |
|
||||
source .azure/linux.bashrc
|
||||
stack install.hs build-latest
|
||||
displayName: Run build-latest target of `install.hs`
|
||||
stack install.hs latest
|
||||
displayName: Run latest target of `install.hs`
|
||||
|
@ -28,59 +28,89 @@ jobs:
|
||||
variables:
|
||||
STACK_ROOT: /home/vsts/.stack
|
||||
steps:
|
||||
- task: CacheBeta@0
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: stack-root | $(Agent.OS) | $(Build.SourcesDirectory)/$(YAML_FILE)
|
||||
key: '"stack-root" | "$(Agent.OS)" | $(Build.SourcesDirectory)/$(YAML_FILE)'
|
||||
path: .azure-cache
|
||||
cacheHitVar: CACHE_RESTORED
|
||||
displayName: "Download cache"
|
||||
- bash: |
|
||||
mkdir -p $STACK_ROOT
|
||||
tar -xzf .azure-cache/stack-root.tar.gz -C /
|
||||
tar -vxzf .azure-cache/stack-root.tar.gz -C /
|
||||
mkdir -p .stack-work
|
||||
tar -xzf .azure-cache/stack-work.tar.gz
|
||||
tar -vxzf .azure-cache/stack-work.tar.gz
|
||||
displayName: "Unpack cache"
|
||||
condition: eq(variables.CACHE_RESTORED, 'true')
|
||||
- bash: |
|
||||
git submodule sync
|
||||
git submodule update --init
|
||||
displayName: Sync submodules
|
||||
- bash: |
|
||||
mkdir -p ~/.local/bin
|
||||
curl -L https://get.haskellstack.org/stable/linux-x86_64.tar.gz | \
|
||||
tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'
|
||||
tar vxz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'
|
||||
displayName: Install stack
|
||||
- bash: |
|
||||
source .azure/linux.bashrc
|
||||
source .azure/linux-stack.bashrc
|
||||
stack setup --stack-yaml $(YAML_FILE)
|
||||
displayName: Install GHC
|
||||
- bash: |
|
||||
source .azure/linux.bashrc
|
||||
source .azure/linux-stack.bashrc
|
||||
stack --stack-yaml $(YAML_FILE) --install-ghc build --only-dependencies
|
||||
displayName: Build dependencies
|
||||
- bash: |
|
||||
source .azure/linux.bashrc
|
||||
source .azure/linux-stack.bashrc
|
||||
stack build --stack-yaml $(YAML_FILE)
|
||||
displayName: Build `hie`
|
||||
- bash: |
|
||||
source .azure/linux.bashrc
|
||||
source .azure/linux-stack.bashrc
|
||||
stack install --stack-yaml $(YAML_FILE) # `hie` binary required locally for tests
|
||||
mkdir .azure-deploy
|
||||
stack install --stack-yaml $(YAML_FILE) --local-bin-path .azure-deploy
|
||||
cd .azure-deploy
|
||||
if [ $YAML_FILE != "stack.yaml" ]; then
|
||||
GHC_MINOR_VERSION=${YAML_FILE:6:5}
|
||||
GHC_MAJOR_VERSION=${YAML_FILE:6:3}
|
||||
cp hie hie-$GHC_MINOR_VERSION
|
||||
cp hie hie-$GHC_MAJOR_VERSION
|
||||
else
|
||||
GHC_MINOR_VERSION=nightly
|
||||
fi
|
||||
stack unpack hlint --stack-yaml ../$(YAML_FILE) --to "$(Agent.TempDirectory)"
|
||||
mkdir -p data
|
||||
cp "$(Agent.TempDirectory)"/hlint*/data/hlint.yaml data
|
||||
ARTIFACT_NAME=hie-$(hie --numeric-version)-ghc-$GHC_MINOR_VERSION-linux-x86_64
|
||||
tar -vczf $(Build.ArtifactStagingDirectory)/$ARTIFACT_NAME.tar.xz *
|
||||
displayName: Install `hie`
|
||||
- bash: |
|
||||
source .azure/linux-stack.bashrc
|
||||
stack build --stack-yaml $(YAML_FILE) --test --bench --only-dependencies
|
||||
stack install --stack-yaml $(YAML_FILE) # `hie` binary required for tests
|
||||
displayName: Build Test-dependencies
|
||||
- bash: |
|
||||
sudo apt update
|
||||
sudo apt install z3
|
||||
displayName: "Install Runtime Test-Dependencies: z3"
|
||||
- bash: |
|
||||
source .azure/linux.bashrc
|
||||
stack install --resolver=lts-11.18 liquid-fixpoint-0.7.0.7 dotgen-0.4.2 fgl-visualize-0.1.0.1 located-base-0.1.1.1 liquidhaskell-0.8.2.4
|
||||
source .azure/linux-stack.bashrc
|
||||
stack install --resolver=lts-13.20 liquid-fixpoint-0.8.0.2 liquidhaskell-0.8.6.2
|
||||
displayName: "Install Runtime Test-Dependencies: liquidhaskell"
|
||||
# - bash: |
|
||||
# source .azure/linux.bashrc
|
||||
# stack test --stack-yaml $(YAML_FILE)
|
||||
# displayName: Run Test
|
||||
- bash: |
|
||||
source .azure/linux-stack.bashrc
|
||||
stack build hoogle --stack-yaml=$(YAML_FILE)
|
||||
stack exec hoogle generate --stack-yaml=$(YAML_FILE)
|
||||
displayName: "Install Runtime Test-Dependencies: hoogle database"
|
||||
- bash: |
|
||||
source .azure/linux-stack.bashrc
|
||||
# cabal is also a test runtime dependency
|
||||
export PATH=/opt/cabal/3.0/bin:$PATH
|
||||
stack test --stack-yaml $(YAML_FILE)
|
||||
displayName: Run Test
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
pathtoPublish: '$(Build.ArtifactStagingDirectory)'
|
||||
artifactName: hie-$(Agent.OS)-$(YAML_FILE)
|
||||
condition: in(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues')
|
||||
- bash: |
|
||||
mkdir -p .azure-cache
|
||||
tar -czf .azure-cache/stack-root.tar.gz $STACK_ROOT
|
||||
tar -czf .azure-cache/stack-work.tar.gz .stack-work
|
||||
tar -vczf .azure-cache/stack-root.tar.gz $STACK_ROOT
|
||||
tar -vczf .azure-cache/stack-work.tar.gz .stack-work
|
||||
displayName: "Pack cache"
|
||||
|
@ -3,10 +3,8 @@ jobs:
|
||||
timeoutInMinutes: 0
|
||||
pool:
|
||||
vmImage: macOS-10.13
|
||||
strategy:
|
||||
matrix:
|
||||
shake:
|
||||
YAML_FILE: install/shake.yaml
|
||||
variables:
|
||||
YAML_FILE: install/shake.yaml
|
||||
steps:
|
||||
- bash: |
|
||||
export STACK_ROOT="$(Build.SourcesDirectory)"/.stack-root
|
||||
@ -32,9 +30,5 @@ jobs:
|
||||
displayName: Run help of `install.hs`
|
||||
- bash: |
|
||||
source .azure/macos.bashrc
|
||||
stack install.hs stack-install-cabal
|
||||
displayName: Run stack-install-cabal target of `install.hs`
|
||||
- bash: |
|
||||
source .azure/macos.bashrc
|
||||
stack install.hs build-latest
|
||||
displayName: Run build-latest target of `install.hs`
|
||||
stack install.hs latest
|
||||
displayName: Run latest target of `install.hs`
|
||||
|
@ -24,17 +24,17 @@ jobs:
|
||||
variables:
|
||||
STACK_ROOT: $(Build.SourcesDirectory)/.stack
|
||||
steps:
|
||||
- task: CacheBeta@0
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: stack-root | $(Agent.OS) | $(Build.SourcesDirectory)/$(YAML_FILE)
|
||||
key: '"stack-root" | "$(Agent.OS)" | $(Build.SourcesDirectory)/$(YAML_FILE)'
|
||||
path: .azure-cache
|
||||
cacheHitVar: CACHE_RESTORED
|
||||
displayName: "Download cache"
|
||||
- bash: |
|
||||
mkdir -p $STACK_ROOT
|
||||
tar -xzf .azure-cache/stack-root.tar.gz -C /
|
||||
tar -vxzf .azure-cache/stack-root.tar.gz -C /
|
||||
mkdir -p .stack-work
|
||||
tar -xzf .azure-cache/stack-work.tar.gz
|
||||
tar -vxzf .azure-cache/stack-work.tar.gz
|
||||
displayName: "Unpack cache"
|
||||
condition: eq(variables.CACHE_RESTORED, 'true')
|
||||
- bash: |
|
||||
@ -44,7 +44,7 @@ jobs:
|
||||
- bash: |
|
||||
mkdir -p ~/.local/bin
|
||||
curl -skL https://get.haskellstack.org/stable/osx-x86_64.tar.gz | \
|
||||
tar xz --strip-components=1 --include '*/stack' -C ~/.local/bin;
|
||||
tar vxz --strip-components=1 --include '*/stack' -C ~/.local/bin;
|
||||
displayName: Install stack
|
||||
- bash: |
|
||||
source .azure/macos.bashrc
|
||||
@ -58,26 +58,66 @@ jobs:
|
||||
source .azure/macos.bashrc
|
||||
stack build --stack-yaml $(YAML_FILE)
|
||||
displayName: Build `hie`
|
||||
- bash: |
|
||||
source .azure/macos.bashrc
|
||||
stack install --stack-yaml $(YAML_FILE) # `hie` binary required locally for tests
|
||||
mkdir .azure-deploy
|
||||
stack install --stack-yaml $(YAML_FILE) --local-bin-path .azure-deploy
|
||||
cd .azure-deploy
|
||||
if [ $YAML_FILE != "stack.yaml" ]; then
|
||||
GHC_MINOR_VERSION=${YAML_FILE:6:5}
|
||||
GHC_MAJOR_VERSION=${YAML_FILE:6:3}
|
||||
cp hie hie-$GHC_MINOR_VERSION
|
||||
cp hie hie-$GHC_MAJOR_VERSION
|
||||
else
|
||||
GHC_MINOR_VERSION=nightly
|
||||
fi
|
||||
stack unpack hlint --stack-yaml ../$(YAML_FILE) --to "$(Agent.TempDirectory)"
|
||||
mkdir -p data
|
||||
cp "$(Agent.TempDirectory)"/hlint*/data/hlint.yaml data
|
||||
ARTIFACT_NAME=hie-$(hie --numeric-version)-ghc-$GHC_MINOR_VERSION-macos-x86_64
|
||||
tar -vczf $(Build.ArtifactStagingDirectory)/$ARTIFACT_NAME.tar.xz *
|
||||
displayName: Install `hie`
|
||||
- bash: |
|
||||
source .azure/macos.bashrc
|
||||
stack build --stack-yaml $(YAML_FILE) --test --bench --only-dependencies
|
||||
stack install --stack-yaml $(YAML_FILE) # `hie` binary required for tests
|
||||
displayName: Build Test-dependencies
|
||||
- bash: |
|
||||
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
||||
brew update
|
||||
brew unlink python@2
|
||||
brew install z3
|
||||
displayName: "Install Runtime Test-Dependencies: z3"
|
||||
- bash: |
|
||||
source .azure/macos.bashrc
|
||||
stack install --resolver=lts-11.18 liquid-fixpoint-0.7.0.7 dotgen-0.4.2 fgl-visualize-0.1.0.1 located-base-0.1.1.1 liquidhaskell-0.8.2.4
|
||||
stack install --resolver=lts-13.20 liquid-fixpoint-0.8.0.2 liquidhaskell-0.8.6.2
|
||||
displayName: "Install Runtime Test-Dependencies: liquidhaskell"
|
||||
# - bash: |
|
||||
# source .azure/macos.bashrc
|
||||
# stack test --stack-yaml $(YAML_FILE)
|
||||
# displayName: Run Test
|
||||
- bash: |
|
||||
source .azure/macos.bashrc
|
||||
stack build hoogle --stack-yaml=$(YAML_FILE)
|
||||
stack exec hoogle generate --stack-yaml=$(YAML_FILE)
|
||||
displayName: "Install Runtime Test-Dependencies: hoogle database"
|
||||
- bash: |
|
||||
source .azure/macos.bashrc
|
||||
brew install cabal-install
|
||||
displayName: "Install Runtime Test-Dependencies: cabal"
|
||||
- bash: |
|
||||
source .azure/macos.bashrc
|
||||
GHC_MAJOR_VERSION=${YAML_FILE:6:3}
|
||||
if [ $GHC_MAJOR_VERSION != "8.4" ]; then
|
||||
stack test --stack-yaml $(YAML_FILE)
|
||||
else
|
||||
#TODO Enable dispatcher-test and func-test for ghc-8.4.*
|
||||
stack test --stack-yaml $(YAML_FILE) :unit-test :plugin-dispatcher-test :wrapper-test
|
||||
fi
|
||||
displayName: Run Test
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
pathtoPublish: '$(Build.ArtifactStagingDirectory)'
|
||||
artifactName: hie-$(Agent.OS)-$(YAML_FILE)
|
||||
condition: in(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues')
|
||||
- bash: |
|
||||
mkdir -p .azure-cache
|
||||
tar -czf .azure-cache/stack-root.tar.gz $STACK_ROOT
|
||||
tar -czf .azure-cache/stack-work.tar.gz .stack-work
|
||||
tar -vczf .azure-cache/stack-root.tar.gz $STACK_ROOT
|
||||
tar -vczf .azure-cache/stack-work.tar.gz .stack-work
|
||||
displayName: "Pack cache"
|
||||
|
@ -6,6 +6,8 @@ jobs:
|
||||
variables:
|
||||
YAML_FILE: install/shake.yaml
|
||||
PROJECT_FILE: install/shake.project
|
||||
STACK_ROOT: "C:\\sr"
|
||||
# TODO: Replace stack with chocolatey for installing ghc and cabal
|
||||
steps:
|
||||
- bash: |
|
||||
curl -sSkL http://www.stackage.org/stack/windows-x86_64 -o /usr/bin/stack.zip
|
||||
@ -17,21 +19,21 @@ jobs:
|
||||
displayName: Install GHC
|
||||
- bash: |
|
||||
source .azure/windows.bashrc
|
||||
stack install cabal-install --stack-yaml $(YAML_FILE)
|
||||
stack install Cabal-3.0.0.0 cabal-install-3.0.0.0 --stack-yaml $(YAML_FILE)
|
||||
displayName: Install `cabal-install`
|
||||
- bash: |
|
||||
source .azure/windows.bashrc
|
||||
cabal update
|
||||
cabal v2-update
|
||||
displayName: update cabal
|
||||
# - bash: |
|
||||
# source .azure/windows.bashrc
|
||||
# stack --stack-yaml $(YAML_FILE) build --only-dependencies
|
||||
# displayName: Build dependencies
|
||||
- bash: |
|
||||
source .azure/windows.bashrc
|
||||
cabal v2-build hie-install -w $(stack path --stack-yaml $(YAML_FILE) --compiler-exe) --project-file $(PROJECT_FILE)
|
||||
displayName: Build `hie-install`
|
||||
- bash: |
|
||||
source .azure/windows.bashrc
|
||||
cabal v2-run install.hs -w $(stack path --stack-yaml $(YAML_FILE) --compiler-exe) --project-file $(PROJECT_FILE) help
|
||||
GHC_PATH=$(stack path --stack-yaml $YAML_FILE --compiler-exe)
|
||||
cabal v2-run install.hs -w $GHC_PATH --project-file $(PROJECT_FILE) help
|
||||
displayName: Run help of `install.hs`
|
||||
# - bash: |
|
||||
# source .azure/windows.bashrc
|
||||
# GHC_PATH=$(stack path --stack-yaml $YAML_FILE --compiler-exe)
|
||||
# GHC_DIR=$(dirname $GHC_PATH)
|
||||
# export PATH=$(cygpath $GHC_DIR):$PATH
|
||||
# cabal v2-run install.hs -w $GHC_PATH --project-file $(PROJECT_FILE) build-latest
|
||||
# displayName: Run build-latest target of `install.hs`
|
||||
|
@ -3,10 +3,9 @@ jobs:
|
||||
timeoutInMinutes: 0
|
||||
pool:
|
||||
vmImage: windows-2019
|
||||
strategy:
|
||||
matrix:
|
||||
shake:
|
||||
YAML_FILE: install/shake.yaml
|
||||
variables:
|
||||
YAML_FILE: install/shake.yaml
|
||||
STACK_ROOT: "C:\\sr"
|
||||
steps:
|
||||
- bash: |
|
||||
curl -sSkL http://www.stackage.org/stack/windows-x86_64 -o /usr/bin/stack.zip
|
||||
@ -18,7 +17,7 @@ jobs:
|
||||
displayName: Install GHC
|
||||
- bash: |
|
||||
source .azure/windows.bashrc
|
||||
stack --stack-yaml $(YAML_FILE) --install-ghc build --only-dependencies
|
||||
stack --stack-yaml $(YAML_FILE) build --only-dependencies
|
||||
displayName: Build dependencies
|
||||
- bash: |
|
||||
source .azure/windows.bashrc
|
||||
@ -30,9 +29,6 @@ jobs:
|
||||
displayName: Run help of `install.hs`
|
||||
- bash: |
|
||||
source .azure/windows.bashrc
|
||||
stack install.hs stack-install-cabal
|
||||
displayName: Run stack-install-cabal target of `install.hs`
|
||||
- bash: |
|
||||
source .azure/windows.bashrc
|
||||
stack install.hs build-latest
|
||||
displayName: Run build-latest target of `install.hs`
|
||||
# Some executions fails with spurious errors installing the exe
|
||||
stack install.hs latest || stack install.hs latest
|
||||
displayName: Run latest target of `install.hs`
|
||||
|
@ -26,19 +26,19 @@ jobs:
|
||||
STACK_ROOT: "C:\\sr"
|
||||
|
||||
steps:
|
||||
- task: CacheBeta@0
|
||||
- task: Cache@2
|
||||
inputs:
|
||||
key: stack-root | $(Agent.OS) | $(Build.SourcesDirectory)/$(YAML_FILE)
|
||||
path: $(STACK_ROOT)
|
||||
key: '"stack-root" | "$(Agent.OS)" | $(Build.SourcesDirectory)/$(YAML_FILE)'
|
||||
path: .azure-cache
|
||||
cacheHitVar: CACHE_RESTORED
|
||||
displayName: "Cache stack-root"
|
||||
- task: CacheBeta@0
|
||||
inputs:
|
||||
key: |
|
||||
"stack-work"
|
||||
$(Agent.OS)
|
||||
$(Build.SourcesDirectory)/$(YAML_FILE)
|
||||
path: .stack-work
|
||||
displayName: "Cache stack-work"
|
||||
- bash: |
|
||||
mkdir -p $STACK_ROOT
|
||||
tar -vxzf .azure-cache/stack-root.tar.gz -C /
|
||||
mkdir -p .stack-work
|
||||
tar -vxzf .azure-cache/stack-work.tar.gz
|
||||
displayName: "Unpack cache"
|
||||
condition: eq(variables.CACHE_RESTORED, 'true')
|
||||
- bash: |
|
||||
git submodule sync
|
||||
git submodule update --init
|
||||
@ -59,10 +59,29 @@ jobs:
|
||||
source .azure/windows.bashrc
|
||||
stack build --stack-yaml $(YAML_FILE)
|
||||
displayName: Build `hie`
|
||||
- bash: |
|
||||
source .azure/windows.bashrc
|
||||
stack install --stack-yaml $(YAML_FILE) # `hie` binary required locally for tests
|
||||
mkdir .azure-deploy
|
||||
stack install --stack-yaml $(YAML_FILE) --local-bin-path .azure-deploy
|
||||
cd .azure-deploy
|
||||
if [ $YAML_FILE != "stack.yaml" ]; then
|
||||
GHC_MINOR_VERSION=${YAML_FILE:6:5}
|
||||
GHC_MAJOR_VERSION=${YAML_FILE:6:3}
|
||||
cp hie.exe hie-$GHC_MINOR_VERSION.exe
|
||||
cp hie.exe hie-$GHC_MAJOR_VERSION.exe
|
||||
else
|
||||
GHC_MINOR_VERSION=nightly
|
||||
fi
|
||||
stack unpack hlint --stack-yaml ../$(YAML_FILE) --to "$(Agent.TempDirectory)"
|
||||
mkdir -p data
|
||||
cp "$(Agent.TempDirectory)"/hlint*/data/hlint.yaml data
|
||||
ARTIFACT_NAME=hie-$(hie --numeric-version)-ghc-$GHC_MINOR_VERSION-windows-x86_64
|
||||
7z a "$(Build.ArtifactStagingDirectory)/$ARTIFACT_NAME.zip" *
|
||||
displayName: Install `hie`
|
||||
- bash: |
|
||||
source .azure/windows.bashrc
|
||||
stack build --stack-yaml $(YAML_FILE) --test --bench --only-dependencies
|
||||
stack install --stack-yaml $(YAML_FILE) # `hie` binary required for tests
|
||||
displayName: Build Test-dependencies
|
||||
- bash: |
|
||||
# TODO: try to install automatically (`choco install z3` fails and pacman is not installed)
|
||||
@ -72,10 +91,41 @@ jobs:
|
||||
displayName: "Install Runtime Test-Dependencies: z3"
|
||||
- bash: |
|
||||
source .azure/windows.bashrc
|
||||
stack install --resolver=lts-11.18 liquid-fixpoint-0.7.0.7 dotgen-0.4.2 fgl-visualize-0.1.0.1 located-base-0.1.1.1 liquidhaskell-0.8.2.4
|
||||
liquid -v
|
||||
stack install --resolver=lts-13.20 liquid-fixpoint-0.8.0.2 liquidhaskell-0.8.6.2
|
||||
displayName: "Install Runtime Test-Dependencies: liquidhaskell"
|
||||
# - bash: |
|
||||
# source .azure/windows.bashrc
|
||||
# stack test --stack-yaml $(YAML_FILE) :unit-test
|
||||
# displayName: Run Test
|
||||
- bash: |
|
||||
source .azure/windows.bashrc
|
||||
stack build hoogle --stack-yaml=$(YAML_FILE)
|
||||
stack exec hoogle generate --stack-yaml=$(YAML_FILE)
|
||||
displayName: "Install Runtime Test-Dependencies: hoogle database"
|
||||
- bash: |
|
||||
source .azure/windows.bashrc
|
||||
choco install cabal
|
||||
/C/ProgramData/chocolatey/bin/RefreshEnv.cmd
|
||||
displayName: "Install Runtime Test-Dependencies: cabal"
|
||||
- bash: |
|
||||
source .azure/windows.bashrc
|
||||
# TODO: Enable CabalHelper unit tests, see https://github.com/DanielG/cabal-helper/issues/91
|
||||
if [ $YAML_FILE = "stack-8.6.4.yaml" ]; then
|
||||
TEST_ARGS=--test-arguments="--skip=CabalHelper"
|
||||
fi
|
||||
# TODO: Enable rest of test suites
|
||||
stack test --stack-yaml $(YAML_FILE) :unit-test :plugin-dispatcher-test $TEST_ARGS # :dispatcher-test :wrapper-test
|
||||
# TODO: Enable failing functional test
|
||||
if [ $YAML_FILE != "stack-8.6.4.yaml" ]; then
|
||||
stack test --stack-yaml $(YAML_FILE) :func-test
|
||||
else
|
||||
stack test --stack-yaml $(YAML_FILE) :func-test --ta="--skip \"/Hover/hover/works\""
|
||||
fi
|
||||
displayName: Run Test
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
pathtoPublish: '$(Build.ArtifactStagingDirectory)'
|
||||
artifactName: hie-$(Agent.OS)-$(YAML_FILE)
|
||||
condition: in(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues')
|
||||
- bash: |
|
||||
mkdir -p .azure-cache
|
||||
tar -vczf .azure-cache/stack-root.tar.gz $(cygpath $STACK_ROOT)
|
||||
tar -vczf .azure-cache/stack-work.tar.gz .stack-work
|
||||
displayName: "Pack cache"
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -36,6 +36,7 @@
|
||||
/docs/source/__pycache__/
|
||||
/docs/source/plugins/
|
||||
/elisp/.cask/
|
||||
/test/testdata/*.exe
|
||||
/test/testdata/FuncTest.refactored.hs
|
||||
/test/testdata/HaReAddRmParam.refactored.hs
|
||||
/test/testdata/HaReCase.refactored.hs
|
||||
|
108
Changelog.md
108
Changelog.md
@ -1,3 +1,111 @@
|
||||
# 1.0.0.0
|
||||
|
||||
- NOTE: 1.0 status does **not** mean it is now stable.
|
||||
- This is a major version bump, because the internal architecture has
|
||||
changed to make use of the new
|
||||
[hie-bios](https://github.com/mpickering/hie-bios) from @mpickering
|
||||
(and a host of others).
|
||||
|
||||
Some of the implications of this are
|
||||
|
||||
- Cabal 3.0 support.
|
||||
- Support for stack scripts.
|
||||
- Work on test, executable, benchmark and library components at the same time.
|
||||
- You can set the build-tool (stack or cabal) explicitly.
|
||||
- Various memory leaks have been fixed.
|
||||
- Various performance improvements.
|
||||
|
||||
Also, until `hie-bios` provides a means to get a full module graph for
|
||||
the project, we haved disabled HaRe (rename, caseSplitCmd, etc...).
|
||||
It is better to not have a tool, than one that sort-of works, and we
|
||||
did not want to hold back all the other work that has been done.
|
||||
|
||||
|
||||
## Changes
|
||||
|
||||
- Fallback to direct cradle if no project context can be found
|
||||
([#1551](https://github.com/haskell/haskell-ide-engine/pull/1551), by @fendor)
|
||||
|
||||
- Bump resolvers `lts-14.20` for GHC 8.6.5.
|
||||
([#1547](https://github.com/haskell/haskell-ide-engine/pull/1547), by @alanz)
|
||||
|
||||
- Improve quality and information density of error message
|
||||
([#1522](https://github.com/haskell/haskell-ide-engine/pull/1522), by @fendor)
|
||||
|
||||
- Add cmd script to install with cabal
|
||||
([#1542](https://github.com/haskell/haskell-ide-engine/pull/1542), by @jneira)
|
||||
|
||||
- Tweak hie-wrapper and hie exe startup messages
|
||||
([#1539](https://github.com/haskell/haskell-ide-engine/pull/1539), by @alanz)
|
||||
|
||||
- First pass at printing out debug info if started without --lsp
|
||||
([#1538](https://github.com/haskell/haskell-ide-engine/pull/1538), by @alanz)
|
||||
|
||||
- Return hlint code actions as type 'quickfix'
|
||||
([#1537](https://github.com/haskell/haskell-ide-engine/pull/1537), by @alanz)
|
||||
|
||||
- Readme: Remove non-existing troubleshooting entry
|
||||
([#1534](https://github.com/haskell/haskell-ide-engine/pull/1534), by @andys8)
|
||||
|
||||
- Add sample hie.yaml files for stack and cabal
|
||||
([#1533](https://github.com/haskell/haskell-ide-engine/pull/1533), by @alanz)
|
||||
|
||||
- Look for stack.yaml before selecting .cabal location as project root
|
||||
([#1531](https://github.com/haskell/haskell-ide-engine/pull/1531), by @fendor)
|
||||
|
||||
- Change Maybe LspFuncs to LspFuncs inside IdeEnv
|
||||
([#1523](https://github.com/haskell/haskell-ide-engine/pull/1523), by @bubba)
|
||||
|
||||
- Replaced cabal-helper submodule with hackage version
|
||||
([#1521](https://github.com/haskell/haskell-ide-engine/pull/1521), by @alanz)
|
||||
|
||||
- Adapt GhcModPluginSpec after merge of #1496
|
||||
([#1507](https://github.com/haskell/haskell-ide-engine/pull/1507), by @alanz)
|
||||
|
||||
- Find the libdir directory of ghc at run-time
|
||||
([#1496](https://github.com/haskell/haskell-ide-engine/pull/1496), by @fendor)
|
||||
|
||||
- Restore the ghcmod plugin command routing
|
||||
([#1505](https://github.com/haskell/haskell-ide-engine/pull/1505), by @alanz)
|
||||
|
||||
- Make errorm use errorM instead of warningM
|
||||
([#1502](https://github.com/haskell/haskell-ide-engine/pull/1502), by @DavSanchez)
|
||||
|
||||
- Refactor plugins and commands now that JSON transport is gone
|
||||
([#1492](https://github.com/haskell/haskell-ide-engine/pull/1492), by @bubba)
|
||||
|
||||
- Retain the --lsp option, to not break existing clients
|
||||
([#1494](https://github.com/haskell/haskell-ide-engine/pull/1494), by @alanz)
|
||||
updated 14 days ago
|
||||
|
||||
- Remove JSON transport
|
||||
([#1489](https://github.com/haskell/haskell-ide-engine/pull/1489), by @bubba)
|
||||
|
||||
- Remove ghc-project-types
|
||||
([#1487](https://github.com/haskell/haskell-ide-engine/pull/1487), by @alanz)
|
||||
|
||||
- Switch back to @DanielG cabal-helper
|
||||
([#1488](https://github.com/haskell/haskell-ide-engine/pull/1488), by @bubba)
|
||||
|
||||
- Update Ubuntu dependency
|
||||
([#1485](https://github.com/haskell/haskell-ide-engine/pull/1485), by @leifmetcalf)
|
||||
|
||||
- Some tidying up for tests
|
||||
([#1483](https://github.com/haskell/haskell-ide-engine/pull/1483), by @bubba)
|
||||
|
||||
- Disable travis for now
|
||||
([#1484](https://github.com/haskell/haskell-ide-engine/pull/1484), by @bubba)
|
||||
|
||||
- Implement the HIE Bios
|
||||
([#1126](https://github.com/haskell/haskell-ide-engine/pull/1126), by @mpickering)
|
||||
|
||||
- Add mac build
|
||||
([#1479](https://github.com/haskell/haskell-ide-engine/pull/1479), by @liam-ly)
|
||||
|
||||
- Unit test over the liquid haskell supported version
|
||||
([#1449](https://github.com/haskell/haskell-ide-engine/pull/1449), by @jneira)
|
||||
|
||||
|
||||
# 0.14.0.0
|
||||
|
||||
- Bump resolvers and deps `lts-14.16` for GHC 8.6.5.
|
||||
|
51
README.md
51
README.md
@ -64,7 +64,6 @@ This project aims to be __the universal interface__ to __a growing number of Has
|
||||
- [Is \<package\> base-x?](#is-package-base-x)
|
||||
- [Is there a hash (#) after \<package\>?](#is-there-a-hash--after-package)
|
||||
- [Otherwise](#otherwise)
|
||||
- [Nix: cabal-helper, No such file or directory](#nix-cabal-helper-no-such-file-or-directory)
|
||||
- [Liquid Haskell](#liquid-haskell)
|
||||
- [Profiling `haskell-ide-engine`.](#profiling-haskell-ide-engine)
|
||||
- [Using `ghc-events-analyze`](#using-ghc-events-analyze)
|
||||
@ -153,6 +152,10 @@ HIE builds from source code, so there's a couple of extra steps.
|
||||
* `stack` must be in your PATH
|
||||
* `git` must be in your PATH
|
||||
* Stack local bin directory must be in your PATH. Get it with `stack path --local-bin`
|
||||
* To make hlint suggestions work, `hie` must locate the [hlint configuration yaml file](https://raw.githubusercontent.com/ndmitchell/hlint/master/data/hlint.yaml). The file is searched in order in the following locations:
|
||||
* In the directory pointed by the environment variable `$HLINT_DATADIR`.
|
||||
* If hie was installed using stack or cabal, in a tool-specific internal installation directory.
|
||||
* And finally in a subdirectory named `data` inside the directory where the `hie` executable is.
|
||||
|
||||
Tip: you can quickly check if some command is in your path by running the command.
|
||||
If you receive some meaningful output instead of "command not found"-like message
|
||||
@ -219,49 +222,43 @@ The install-script can be invoked via `cabal` instead of `stack` with the comman
|
||||
cabal v2-run ./install.hs --project-file install/shake.project <target>
|
||||
```
|
||||
|
||||
Running the script with cabal on windows requires a cabal version greater or equal to `3.0.0.0`.
|
||||
|
||||
Unfortunately, it is still required to have `stack` installed so that the install-script can locate the `local-bin` directory (on Linux `~/.local/bin`) and copy the `hie` binaries to `hie-x.y.z`, which is required for the `hie-wrapper` to function as expected. There are plans to remove this requirement and let users build hie only with one build tool or another.
|
||||
|
||||
For brevity, only the `stack`-based commands are presented in the following sections.
|
||||
|
||||
##### Install cabal using stack
|
||||
|
||||
Although you can use hie for stack based projects (those which have a `stack.yaml` in the project base directory) without having cabal installed, you will need it for cabal based projects (with only a `<projectName>.cabal` file or a `cabal.project` one in the project base directory).
|
||||
|
||||
You can install an appropriate cabal version using stack by running:
|
||||
or using the existing alias script
|
||||
|
||||
```bash
|
||||
stack ./install.hs stack-install-cabal
|
||||
./cabal-hie-install <target>
|
||||
```
|
||||
|
||||
Running the script with cabal on windows requires a cabal version greater or equal to `3.0.0.0`.
|
||||
|
||||
For brevity, only the `stack`-based commands are presented in the following sections.
|
||||
|
||||
##### Install specific GHC Version
|
||||
|
||||
Install hie for the latest available and supported GHC version (and hoogle docs):
|
||||
|
||||
```bash
|
||||
stack ./install.hs build
|
||||
stack ./install.hs hie
|
||||
```
|
||||
|
||||
Install hie for a specific GHC version (and hoogle docs):
|
||||
|
||||
```bash
|
||||
stack ./install.hs hie-8.6.5
|
||||
stack ./install.hs build-data
|
||||
stack ./install.hs data
|
||||
```
|
||||
|
||||
The Haskell IDE Engine can also be built with `cabal v2-build` instead of `stack build`.
|
||||
This has the advantage that you can decide how the GHC versions have been installed.
|
||||
To see what GHC versions are available, the command `stack install.hs cabal-ghcs` can be used.
|
||||
To see what GHC versions are available, the command `cabal-hie-install ghcs` can be used.
|
||||
It will list all GHC versions that are on the path and their respective installation directory.
|
||||
If you think, this list is incomplete, you can try to modify the PATH variable, such that the executables can be found.
|
||||
Note, that the targets `cabal-build` and `cabal-build-data` depend on the found GHC versions.
|
||||
Note, that the targets `hie` and `data` depend on the found GHC versions.
|
||||
They install Haskell IDE Engine only for the found GHC versions.
|
||||
|
||||
An example output is:
|
||||
|
||||
```bash
|
||||
> stack install.hs cabal-ghcs
|
||||
> cabal-hie-install ghcs
|
||||
******************************************************************
|
||||
Found the following GHC paths:
|
||||
ghc-8.4.4: /opt/bin/ghc-8.4.4
|
||||
@ -273,11 +270,11 @@ ghc-8.6.2: /opt/bin/ghc-8.6.2
|
||||
If your desired ghc has been found, you use it to install Haskell IDE Engine.
|
||||
|
||||
```bash
|
||||
stack install.hs cabal-hie-8.4.4
|
||||
stack install.hs cabal-build-data
|
||||
cabal-hie-install hie-8.4.4
|
||||
cabal-hie-install data
|
||||
```
|
||||
|
||||
In general, targets that use `cabal` instead of `stack` are prefixed with `cabal-*` and are identical to their counterpart, except they do not install a GHC if it is missing but fail.
|
||||
In general, executing targets with `cabal` instead of `stack` have the same behaviour, except they do not install a GHC if it is missing but fail.
|
||||
|
||||
##### Multiple versions of HIE (optional)
|
||||
|
||||
@ -541,6 +538,7 @@ Then issue `:CocConfig` and add the following to your Coc config file.
|
||||
"languageserver": {
|
||||
"haskell": {
|
||||
"command": "hie-wrapper",
|
||||
"args": ["--lsp"],
|
||||
"rootPatterns": [
|
||||
"*.cabal",
|
||||
"stack.yaml",
|
||||
@ -583,7 +581,7 @@ into `~/.vim/pack/XXX/start/`, where `XXX` is just a name for your "plugin suite
|
||||
|
||||
```vim
|
||||
set rtp+=~/.vim/pack/XXX/start/LanguageClient-neovim
|
||||
let g:LanguageClient_serverCommands = { 'haskell': ['hie-wrapper'] }
|
||||
let g:LanguageClient_serverCommands = { 'haskell': ['hie-wrapper', '--lsp'] }
|
||||
```
|
||||
|
||||
You'll probably want to add some mappings for common commands:
|
||||
@ -726,6 +724,15 @@ This project is not started from scratch:
|
||||
- Fork this repo and hack as much as you can.
|
||||
- Ask @alanz or @hvr to join the project.
|
||||
|
||||
### Hacking on haskell-ide-engine
|
||||
|
||||
Haskell-ide-engine can be used on its own project. We have supplied
|
||||
preset samples of `hie.yaml` files for stack and cabal, simply copy
|
||||
the appropriate template to `hie.yaml` and it shoule work.
|
||||
|
||||
- `hie.yaml.cbl` for cabal
|
||||
- `hie.yaml.stack` for stack
|
||||
|
||||
## Documentation
|
||||
|
||||
All the documentation is in [the docs folder](/docs) at the root of this project.
|
||||
|
@ -72,6 +72,8 @@ run opts = do
|
||||
d <- getCurrentDirectory
|
||||
logm $ "Current directory:" ++ d
|
||||
logm $ "Operating system:" ++ os
|
||||
args <- getArgs
|
||||
logm $ "args:" ++ show args
|
||||
|
||||
-- Get the cabal directory from the cradle
|
||||
cradle <- findLocalCradle (d </> "File.hs")
|
||||
@ -99,7 +101,6 @@ run opts = do
|
||||
Nothing -> logm $ "cannot find any hie exe, looked for:" ++ intercalate ", " candidates
|
||||
Just e -> do
|
||||
logm $ "found hie exe at:" ++ e
|
||||
args <- getArgs
|
||||
logm $ "args:" ++ show args
|
||||
logm "launching ....\n\n\n"
|
||||
callProcess e args
|
||||
|
120
app/MainHie.hs
120
app/MainHie.hs
@ -3,11 +3,17 @@
|
||||
{-# LANGUAGE RankNTypes #-}
|
||||
module Main where
|
||||
|
||||
import qualified Control.Exception as E
|
||||
import Control.Monad
|
||||
#if __GLASGOW_HASKELL__ < 808
|
||||
import Data.Monoid ((<>))
|
||||
#endif
|
||||
import Data.Version (showVersion)
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.IO as T
|
||||
import qualified Data.Yaml as Yaml
|
||||
import HIE.Bios.Types
|
||||
import Haskell.Ide.Engine.Cradle (findLocalCradle, cradleDisplay, getProjectGhcLibDir)
|
||||
import Haskell.Ide.Engine.MonadFunctions
|
||||
import Haskell.Ide.Engine.MonadTypes
|
||||
import Haskell.Ide.Engine.Options
|
||||
@ -19,9 +25,14 @@ import Options.Applicative.Simple
|
||||
import qualified Paths_haskell_ide_engine as Meta
|
||||
import System.Directory
|
||||
import System.Environment
|
||||
import qualified System.Log.Logger as L
|
||||
import HIE.Bios.Types
|
||||
import System.FilePath
|
||||
import System.Info
|
||||
import System.IO
|
||||
import qualified System.Log.Logger as L
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
import RunTest
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- plugins
|
||||
@ -29,6 +40,9 @@ import System.IO
|
||||
import Haskell.Ide.Engine.Plugin.ApplyRefact
|
||||
import Haskell.Ide.Engine.Plugin.Brittany
|
||||
import Haskell.Ide.Engine.Plugin.Example2
|
||||
import Haskell.Ide.Engine.Plugin.Floskell
|
||||
import Haskell.Ide.Engine.Plugin.Generic
|
||||
import Haskell.Ide.Engine.Plugin.GhcMod
|
||||
-- import Haskell.Ide.Engine.Plugin.HaRe
|
||||
import Haskell.Ide.Engine.Plugin.Haddock
|
||||
import Haskell.Ide.Engine.Plugin.HfaAlign
|
||||
@ -36,9 +50,6 @@ import Haskell.Ide.Engine.Plugin.HsImport
|
||||
import Haskell.Ide.Engine.Plugin.Liquid
|
||||
import Haskell.Ide.Engine.Plugin.Package
|
||||
import Haskell.Ide.Engine.Plugin.Pragmas
|
||||
import Haskell.Ide.Engine.Plugin.Floskell
|
||||
import Haskell.Ide.Engine.Plugin.Generic
|
||||
import Haskell.Ide.Engine.Plugin.GhcMod
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
@ -113,23 +124,90 @@ run opts = do
|
||||
maybe (pure ()) setCurrentDirectory $ projectRoot opts
|
||||
|
||||
progName <- getProgName
|
||||
logm $ "Run entered for HIE(" ++ progName ++ ") " ++ hieVersion
|
||||
logm $ "Current directory:" ++ origDir
|
||||
args <- getArgs
|
||||
logm $ "args:" ++ show args
|
||||
|
||||
let initOpts = defaultCradleOpts { cradleOptsVerbosity = verbosity }
|
||||
verbosity = if optBiosVerbose opts then Verbose else Silent
|
||||
|
||||
|
||||
when (optBiosVerbose opts) $
|
||||
logm "Enabling verbose mode for hie-bios. This option currently doesn't do anything."
|
||||
|
||||
when (optExamplePlugin opts) $
|
||||
logm "Enabling Example2 plugin, will insert constant diagnostics etc."
|
||||
|
||||
let plugins' = plugins (optExamplePlugin opts)
|
||||
|
||||
-- launch the dispatcher.
|
||||
scheduler <- newScheduler plugins' initOpts
|
||||
server scheduler origDir plugins' (optCaptureFile opts)
|
||||
if optLsp opts
|
||||
then do
|
||||
-- Start up in LSP mode
|
||||
logm $ "Run entered for HIE(" ++ progName ++ ") " ++ hieVersion
|
||||
logm $ "Operating as a LSP server on stdio"
|
||||
logm $ "Current directory:" ++ origDir
|
||||
logm $ "Operating system:" ++ os
|
||||
logm $ "args:" ++ show args
|
||||
|
||||
let initOpts = defaultCradleOpts { cradleOptsVerbosity = verbosity }
|
||||
verbosity = if optBiosVerbose opts then Verbose else Silent
|
||||
|
||||
|
||||
when (optBiosVerbose opts) $
|
||||
logm "Enabling verbose mode for hie-bios. This option currently doesn't do anything."
|
||||
|
||||
when (optExamplePlugin opts) $
|
||||
logm "Enabling Example2 plugin, will insert constant diagnostics etc."
|
||||
|
||||
-- launch the dispatcher.
|
||||
scheduler <- newScheduler plugins' initOpts
|
||||
server scheduler origDir plugins' (optCaptureFile opts)
|
||||
else do
|
||||
-- Provide debug info
|
||||
cliOut $ "Running HIE(" ++ progName ++ ")"
|
||||
cliOut $ " " ++ hieVersion
|
||||
cliOut $ "To run as a LSP server on stdio, provide the '--lsp' argument"
|
||||
cliOut $ "Current directory:" ++ origDir
|
||||
-- args <- getArgs
|
||||
cliOut $ "\nargs:" ++ show args
|
||||
|
||||
cliOut $ "\nLooking for project config cradle...\n"
|
||||
|
||||
ecradle <- getCradleInfo origDir
|
||||
case ecradle of
|
||||
Left e -> cliOut $ "Could not get cradle:" ++ show e
|
||||
Right cradle -> do
|
||||
projGhc <- getProjectGhcVersion cradle
|
||||
mlibdir <- getProjectGhcLibDir cradle
|
||||
cliOut "\n\n###################################################\n"
|
||||
cliOut $ "Cradle: " ++ cradleDisplay cradle
|
||||
cliOut $ "Project Ghc version: " ++ projGhc
|
||||
cliOut $ "Libdir: " ++ show mlibdir
|
||||
cliOut "Searching for Haskell source files..."
|
||||
targets <- case optFiles opts of
|
||||
[] -> findAllSourceFiles origDir
|
||||
xs -> concat <$> mapM findAllSourceFiles xs
|
||||
|
||||
cliOut $ "Found " ++ show (length targets) ++ " Haskell source files.\n"
|
||||
cliOut "###################################################"
|
||||
cliOut "\nFound the following files:\n"
|
||||
mapM_ cliOut targets
|
||||
cliOut ""
|
||||
|
||||
unless (optDryRun opts) $ do
|
||||
cliOut "\nLoad them all now. This may take a very long time.\n"
|
||||
loadDiagnostics <- runServer mlibdir plugins' targets
|
||||
|
||||
cliOut ""
|
||||
cliOut "###################################################"
|
||||
cliOut "###################################################"
|
||||
cliOut "\nDumping diagnostics:\n\n"
|
||||
mapM_ (cliOut' . uncurry prettyPrintDiags) loadDiagnostics
|
||||
cliOut "\n\nNote: loading of 'Setup.hs' is not supported."
|
||||
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
getCradleInfo :: FilePath -> IO (Either Yaml.ParseException Cradle)
|
||||
getCradleInfo currentDir = do
|
||||
let dummyCradleFile = currentDir </> "File.hs"
|
||||
cradleRes <- E.try (findLocalCradle dummyCradleFile)
|
||||
return cradleRes
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
cliOut :: String -> IO ()
|
||||
cliOut = putStrLn
|
||||
|
||||
cliOut' :: T.Text -> IO ()
|
||||
cliOut' = T.putStrLn
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
|
128
app/RunTest.hs
Normal file
128
app/RunTest.hs
Normal file
@ -0,0 +1,128 @@
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
{-# LANGUAGE TupleSections #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
module RunTest
|
||||
( findAllSourceFiles
|
||||
, compileTarget
|
||||
, runServer
|
||||
, prettyPrintDiags
|
||||
)
|
||||
where
|
||||
|
||||
import GhcMonad
|
||||
import qualified GHC
|
||||
import Control.Monad
|
||||
import qualified Control.Concurrent.STM as STM
|
||||
import Data.List ( isPrefixOf )
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Map as Map
|
||||
import Data.Default
|
||||
import System.Directory ( doesDirectoryExist
|
||||
, listDirectory
|
||||
, canonicalizePath
|
||||
, doesFileExist
|
||||
)
|
||||
import System.FilePath
|
||||
import Language.Haskell.LSP.Core
|
||||
import Language.Haskell.LSP.Types
|
||||
import Haskell.Ide.Engine.PluginsIdeMonads
|
||||
hiding ( withIndefiniteProgress
|
||||
, withProgress
|
||||
)
|
||||
import Haskell.Ide.Engine.GhcModuleCache
|
||||
import qualified Haskell.Ide.Engine.ModuleCache
|
||||
as MC
|
||||
import qualified Haskell.Ide.Engine.Ghc as Ghc
|
||||
|
||||
findAllSourceFiles :: FilePath -> IO [FilePath]
|
||||
findAllSourceFiles fp = do
|
||||
absFp <- canonicalizePath fp
|
||||
isDir <- doesDirectoryExist absFp
|
||||
if isDir
|
||||
then findFilesRecursively
|
||||
isHaskellSource
|
||||
(\path -> any (\p -> p path) [isHidden, isSpecialDir])
|
||||
absFp
|
||||
else filterM doesFileExist [absFp]
|
||||
where
|
||||
isHaskellSource = (== ".hs") . takeExtension
|
||||
isHidden = ("." `isPrefixOf`) . takeFileName
|
||||
isSpecialDir = (== "dist-newstyle") . takeFileName
|
||||
|
||||
findFilesRecursively
|
||||
:: (FilePath -> Bool) -> (FilePath -> Bool) -> FilePath -> IO [FilePath]
|
||||
findFilesRecursively p exclude dir = do
|
||||
dirContents' <- listDirectory dir
|
||||
let dirContents = map (dir </>) dirContents'
|
||||
|
||||
files <- forM dirContents $ \fp -> do
|
||||
isDirectory <- doesDirectoryExist fp
|
||||
if isDirectory
|
||||
then if not $ exclude fp
|
||||
then findFilesRecursively p exclude fp
|
||||
else return []
|
||||
else if p fp then return [fp] else return []
|
||||
|
||||
return $ concat files
|
||||
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
compileTarget
|
||||
:: GHC.DynFlags
|
||||
-> FilePath
|
||||
-> IdeGhcM (IdeResult (Ghc.Diagnostics, Ghc.AdditionalErrs))
|
||||
compileTarget dynFlags fp = do
|
||||
let pubDiags _ _ _ = return ()
|
||||
let defAction = return (mempty, mempty)
|
||||
let action = Ghc.setTypecheckedModule (filePathToUri fp)
|
||||
actionResult <- MC.runActionWithContext pubDiags
|
||||
dynFlags
|
||||
(Just fp)
|
||||
defAction
|
||||
action
|
||||
return $ join actionResult
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
runServer
|
||||
:: Maybe FilePath
|
||||
-> IdePlugins
|
||||
-> [FilePath]
|
||||
-> IO [(FilePath, IdeResult (Ghc.Diagnostics, Ghc.AdditionalErrs))]
|
||||
runServer mlibdir ideplugins targets = do
|
||||
let initialState = IdeState emptyModuleCache Map.empty Map.empty Nothing
|
||||
stateVar <- STM.newTVarIO initialState
|
||||
|
||||
runIdeGhcM mlibdir ideplugins dummyLspFuncs stateVar $ do
|
||||
dynFlags <- getSessionDynFlags
|
||||
mapM (\fp -> (fp, ) <$> compileTarget dynFlags fp) targets
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
prettyPrintDiags
|
||||
:: FilePath -> IdeResult (Ghc.Diagnostics, Ghc.AdditionalErrs) -> T.Text
|
||||
prettyPrintDiags fp diags = T.pack fp <> ": " <> case diags of
|
||||
IdeResultFail IdeError { ideMessage } -> "FAILED\n\t" <> ideMessage
|
||||
IdeResultOk (_diags, errs) ->
|
||||
if null errs then "OK" else T.unlines (map (T.append "\t") errs)
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
dummyLspFuncs :: Default a => LspFuncs a
|
||||
dummyLspFuncs = LspFuncs
|
||||
{ clientCapabilities = def
|
||||
, config = return (Just def)
|
||||
, sendFunc = const (return ())
|
||||
, getVirtualFileFunc = const (return Nothing)
|
||||
, persistVirtualFileFunc = \uri ->
|
||||
return (uriToFilePath (fromNormalizedUri uri))
|
||||
, reverseFileMapFunc = return id
|
||||
, publishDiagnosticsFunc = mempty
|
||||
, flushDiagnosticsBySourceFunc = mempty
|
||||
, getNextReqId = pure (IdInt 0)
|
||||
, rootPath = Nothing
|
||||
, getWorkspaceFolders = return Nothing
|
||||
, withProgress = \_ _ f -> f (const (return ()))
|
||||
, withIndefiniteProgress = \_ _ f -> f
|
||||
}
|
@ -1,7 +1,35 @@
|
||||
# Build master, branches starting with `azure` and tags commits
|
||||
trigger:
|
||||
batch: false
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
- azure*
|
||||
tags:
|
||||
include:
|
||||
- '*'
|
||||
paths:
|
||||
exclude:
|
||||
- .circleci
|
||||
- docs
|
||||
- licenses
|
||||
- logos
|
||||
- LICENSE
|
||||
- '*.md'
|
||||
|
||||
# Enable PR triggers that target the master branch
|
||||
pr:
|
||||
autoCancel: true # cancel previous builds on push
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
- template: ./.azure/linux-stack.yml
|
||||
- template: ./.azure/linux-cabal.yml
|
||||
- template: ./.azure/windows-stack.yml
|
||||
- template: ./.azure/macos-stack.yml
|
||||
- template: ./.azure/linux-installhs-stack.yml
|
||||
- template: ./.azure/windows-installhs-stack.yml
|
||||
- template: ./.azure/windows-installhs-cabal.yml
|
||||
- template: ./.azure/macos-installhs-stack.yml
|
||||
|
2
cabal-hie-install
Normal file
2
cabal-hie-install
Normal file
@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
cabal v2-run ./install.hs --project-file install/shake.project $@
|
1
cabal-hie-install.cmd
Normal file
1
cabal-hie-install.cmd
Normal file
@ -0,0 +1 @@
|
||||
@cabal v2-run .\install.hs --project-file=install\shake.project %*
|
@ -9,4 +9,11 @@ tests: true
|
||||
package haskell-ide-engine
|
||||
test-show-details: direct
|
||||
|
||||
-- Match the flag settings we use in stac builds
|
||||
constraints:
|
||||
haskell-ide-engine +pedantic
|
||||
hie-plugin-api +pedantic
|
||||
|
||||
write-ghc-environment-files: never
|
||||
|
||||
index-state: 2020-01-07T22:09:46Z
|
||||
|
@ -27,18 +27,17 @@ See the project's `README` for detailed information about installing `hie`.
|
||||
The build script `install.hs` defines several targets using the `shake` build system. The targets are roughly:
|
||||
|
||||
* `hie-*`: builds and installs the `hie` binaries. Also renames the binaries to contain the correct version-number.
|
||||
* `build-latest`: builds and installs `hie` for the latest available and supported `ghc` version.
|
||||
* `build-data`: builds the hoogle-db required by `hie`
|
||||
* `build`: builds and installs `hie` for the latest supported `ghc` version (like `build-latest`) and the hoogle-db (like `build-data`)
|
||||
* `cabal-*`: execute the same task as the original target, but with `cabal` instead of `stack`
|
||||
* `latest`: builds and installs `hie` for the latest available and supported `ghc` version.
|
||||
* `data`: builds the hoogle-db required by `hie`
|
||||
* `hie`: builds and installs `hie` for the latest supported `ghc` version (like `latest`) and the hoogle-db (like `data`)
|
||||
|
||||
Each `stack-*.yaml` contains references to packages in the submodules. Calling `stack` with one of those causes the build to fail if the submodules have not been initialized already. The file `shake.yaml` solves this issue invoking the `git` binary itself to update the submodules. Moreover, it specifies the correct version of `shake` and is used for installing all run-time dependencies such as `cabal` and `hoogle` if necessary.
|
||||
Each `stack-*.yaml` contains references to packages in the submodules. Calling `stack` with one of those causes the build to fail if the submodules have not been initialized already. The file `shake.yaml` solves this issue invoking the `git` binary itself to update the submodules. Moreover, it specifies the correct version of `shake` and is used for installing all run-time dependencies such as `hoogle` if necessary.
|
||||
|
||||
### Run-time dependencies
|
||||
|
||||
`hie` depends on a correct environment in order to function properly:
|
||||
|
||||
* `cabal-install`: This dependency is required by `hie` to handle correctly projects that are not `stack` based. You can install an appropriate version using `stack` with the `stack-install-cabal` target.
|
||||
* `cabal-install`: This dependency is required by `hie` to handle correctly projects that are not `stack` based. You can install it using one of the methods listed here: https://www.haskell.org/cabal/#install-upgrade
|
||||
* The `hoogle` database: `hoogle generate` needs to be called with the most-recent `hoogle` version.
|
||||
|
||||
### Steps to build `hie`
|
||||
@ -68,12 +67,21 @@ EOF
|
||||
|
||||
Then `hie` can be compiled for a specific GHC version:
|
||||
|
||||
* For cabal prior to 3.0.0.0
|
||||
```bash
|
||||
export GHCP=<path-to-ghc-binary>
|
||||
cabal v2-install exe:hie -w $GHCP \
|
||||
--write-ghc-environment-files=never --symlink-bindir=$HOME/.local/bin \
|
||||
--overwrite-policy=always --reinstall
|
||||
```
|
||||
* For cabal 3.0.0.0 or newer
|
||||
```bash
|
||||
export GHCP=<path-to-ghc-binary>
|
||||
cabal v2-install exe:hie -w $GHCP \
|
||||
--write-ghc-environment-files=never --installdir=$HOME/.local/bin \
|
||||
--overwrite-policy=always --reinstall
|
||||
```
|
||||
* For windows you will need cabal 3.0.0.0 and add the argument `--install-method=copy`
|
||||
|
||||
The final step is to configure the `hie` client to use a custom `hie-wrapper` script that enables the runtime options for profiling. Such a script could look like this:
|
||||
|
||||
@ -88,17 +96,13 @@ The final step is to configure the `hie` client to use a custom `hie-wrapper` sc
|
||||
|
||||
The `install.hs` script performs some checks to ensure that a correct installation is possible and provide meaningful error messages for known issues.
|
||||
|
||||
* `stack` needs to be up-to-date. Version `1.9.3` is required
|
||||
* `stack` needs to be up-to-date. Version `2.1.1` is required
|
||||
* `cabal` needs to be up-to-date. Version `3.0.0.0` is required for windows systems and `2.4.1.0` for other ones.
|
||||
* `ghc-8.6.3` is broken on windows. Trying to install `hie-8.6.3` on windows is not possible.
|
||||
* When the build fails, an error message, that suggests to remove `.stack-work` directory, is displayed.
|
||||
|
||||
### Tradeoffs
|
||||
|
||||
#### `stack` is a build dependency
|
||||
|
||||
Currently, `stack` is needed even if you run the script with `cabal` to get the path where install the binaries but there are plans to remove that dependency (see #1380).
|
||||
|
||||
#### run `install.hs` with `stack` installs a GHC before running
|
||||
|
||||
Before the code in `install.hs` can be executed, `stack` installs a `GHC`, depending on the `resolver` field in `shake.yaml`. This is necessary if `install.hs` should be completely functional right after a fresh `git clone` without further configuration.
|
||||
|
@ -16,6 +16,7 @@ cabal-version: >=2.0
|
||||
flag pedantic
|
||||
Description: Enable -Werror
|
||||
Default: False
|
||||
Manual: True
|
||||
|
||||
library
|
||||
hs-source-dirs: src
|
||||
@ -47,6 +48,7 @@ library
|
||||
Haskell.Ide.Engine.Types
|
||||
Haskell.Ide.Engine.Version
|
||||
other-modules: Paths_haskell_ide_engine
|
||||
autogen-modules: Paths_haskell_ide_engine
|
||||
build-depends: Cabal >= 1.22
|
||||
, Diff
|
||||
-- , HaRe
|
||||
@ -72,7 +74,7 @@ library
|
||||
, haskell-lsp == 0.19.*
|
||||
, haskell-lsp-types == 0.19.*
|
||||
, haskell-src-exts
|
||||
, hie-plugin-api
|
||||
, hie-plugin-api >= 1.0
|
||||
, hoogle >= 5.0.13
|
||||
, hsimport
|
||||
, hslogger
|
||||
@ -108,16 +110,24 @@ library
|
||||
executable hie
|
||||
hs-source-dirs: app
|
||||
main-is: MainHie.hs
|
||||
other-modules: Paths_haskell_ide_engine
|
||||
other-modules: Paths_haskell_ide_engine, RunTest
|
||||
autogen-modules: Paths_haskell_ide_engine
|
||||
build-depends: base
|
||||
, containers
|
||||
, data-default
|
||||
, directory
|
||||
, filepath
|
||||
, ghc
|
||||
, hie-bios
|
||||
, haskell-ide-engine
|
||||
, haskell-lsp
|
||||
, haskell-lsp-types
|
||||
, hie-plugin-api
|
||||
, hslogger
|
||||
, optparse-simple
|
||||
, stm
|
||||
, text
|
||||
, yaml
|
||||
ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall -Wredundant-constraints
|
||||
-with-rtsopts=-T
|
||||
if flag(pedantic)
|
||||
@ -129,6 +139,7 @@ executable hie-wrapper
|
||||
hs-source-dirs: app
|
||||
main-is: HieWrapper.hs
|
||||
other-modules: Paths_haskell_ide_engine
|
||||
autogen-modules: Paths_haskell_ide_engine
|
||||
build-depends: base
|
||||
, directory
|
||||
, filepath
|
||||
@ -176,6 +187,7 @@ test-suite unit-test
|
||||
hs-source-dirs: test/unit
|
||||
main-is: Main.hs
|
||||
other-modules: ApplyRefactPluginSpec
|
||||
CabalHelperSpec
|
||||
CodeActionsSpec
|
||||
ContextSpec
|
||||
DiffSpec
|
||||
@ -192,6 +204,7 @@ test-suite unit-test
|
||||
build-tool-depends: cabal-helper:cabal-helper-main, hspec-discover:hspec-discover
|
||||
build-depends: QuickCheck
|
||||
, aeson
|
||||
, cabal-helper
|
||||
, ghc
|
||||
, base
|
||||
, bytestring
|
||||
|
@ -13,14 +13,14 @@ import Distribution.Helper (Package, projectPackages, pUnits,
|
||||
unChModuleName, Ex(..), ProjLoc(..),
|
||||
QueryEnv, mkQueryEnv, runQuery,
|
||||
Unit, unitInfo, uiComponents,
|
||||
ChEntrypoint(..))
|
||||
ChEntrypoint(..), UnitInfo(..))
|
||||
import Distribution.Helper.Discover (findProjects, getDefaultDistDir)
|
||||
import Data.Char (toLower)
|
||||
import Data.Function ((&))
|
||||
import Data.List (isPrefixOf, isInfixOf, sortOn, find)
|
||||
import qualified Data.List.NonEmpty as NonEmpty
|
||||
import Data.List.NonEmpty (NonEmpty)
|
||||
import qualified Data.Map as M
|
||||
import qualified Data.Map as Map
|
||||
import Data.Maybe (listToMaybe, mapMaybe, isJust)
|
||||
import Data.Ord (Down(..))
|
||||
import Data.String (IsString(..))
|
||||
@ -45,31 +45,36 @@ import System.Process (readCreateProcessWithExitCode, shell)
|
||||
findLocalCradle :: FilePath -> IO Cradle
|
||||
findLocalCradle fp = do
|
||||
cradleConf <- BIOS.findCradle fp
|
||||
case cradleConf of
|
||||
Just yaml -> BIOS.loadCradle yaml
|
||||
crdl <- case cradleConf of
|
||||
Just yaml -> do
|
||||
debugm $ "Found \"" ++ yaml ++ "\" for \"" ++ fp ++ "\""
|
||||
BIOS.loadCradle yaml
|
||||
Nothing -> cabalHelperCradle fp
|
||||
logm $ "Module \"" ++ fp ++ "\" is loaded by Cradle: " ++ show crdl
|
||||
return crdl
|
||||
|
||||
-- | Check if the given cradle is a stack cradle.
|
||||
-- This might be used to determine the GHC version to use on the project.
|
||||
-- If it is a stack-cradle, we have to use `stack path --compiler-exe`
|
||||
-- If it is a stack-cradle, we have to use @"stack path --compiler-exe"@
|
||||
-- otherwise we may ask `ghc` directly what version it is.
|
||||
isStackCradle :: Cradle -> Bool
|
||||
isStackCradle = (`elem` ["stack", "Cabal-Helper-Stack", "Cabal-Helper-Stack-None"])
|
||||
. BIOS.actionName
|
||||
. BIOS.cradleOptsProg
|
||||
|
||||
-- | Check if the given cradle is a cabal cradle.
|
||||
-- | Check if the given cradle is a cabal cradle.
|
||||
-- This might be used to determine the GHC version to use on the project.
|
||||
-- If it is a stack-cradle, we have to use `stack path --compiler-exe`
|
||||
-- otherwise we may ask `ghc` directly what version it is.
|
||||
-- If it is a stack-cradle, we have to use @"stack path --compiler-exe"@
|
||||
-- otherwise we may ask @ghc@ directly what version it is.
|
||||
isCabalCradle :: Cradle -> Bool
|
||||
isCabalCradle =
|
||||
(`elem`
|
||||
["cabal"
|
||||
[ "cabal"
|
||||
, "Cabal-Helper-Cabal-V1"
|
||||
, "Cabal-Helper-Cabal-V2"
|
||||
, "Cabal-Helper-Cabal-V1-Dir"
|
||||
, "Cabal-Helper-Cabal-V2-Dir"
|
||||
, "Cabal-Helper-Cabal-V2-None"
|
||||
, "Cabal-Helper-Cabal-None"
|
||||
]
|
||||
)
|
||||
@ -78,10 +83,10 @@ isCabalCradle =
|
||||
|
||||
-- | Execute @ghc@ that is based on the given cradle.
|
||||
-- Output must be a single line. If an error is raised, e.g. the command
|
||||
-- failed, a @Nothing@ is returned.
|
||||
-- failed, a 'Nothing' is returned.
|
||||
-- The exact error is written to logs.
|
||||
--
|
||||
-- E.g. for a stack cradle, we use `stack ghc` and for a cabal cradle
|
||||
-- E.g. for a stack cradle, we use @stack ghc@ and for a cabal cradle
|
||||
-- we are taking the @ghc@ that is on the path.
|
||||
execProjectGhc :: Cradle -> [String] -> IO (Maybe String)
|
||||
execProjectGhc crdl args = do
|
||||
@ -143,7 +148,7 @@ getProjectGhcLibDir :: Cradle -> IO (Maybe FilePath)
|
||||
getProjectGhcLibDir crdl =
|
||||
execProjectGhc crdl ["--print-libdir"] >>= \case
|
||||
Nothing -> do
|
||||
logm "Could not obtain the libdir."
|
||||
errorm "Could not obtain the libdir."
|
||||
return Nothing
|
||||
mlibdir -> return mlibdir
|
||||
|
||||
@ -164,6 +169,8 @@ This guessing has no guarantees and may change at any time.
|
||||
=== Example:
|
||||
|
||||
Assume the following project structure:
|
||||
|
||||
@
|
||||
/
|
||||
└── Foo/
|
||||
├── Foo.cabal
|
||||
@ -175,33 +182,39 @@ Assume the following project structure:
|
||||
├── B.cabal
|
||||
└── src/
|
||||
└── Lib2.hs
|
||||
@
|
||||
|
||||
Assume the call @findCabalHelperEntryPoint "/Foo/B/src/Lib2.hs"@.
|
||||
We now want to know to which project "/Foo/B/src/Lib2.hs" belongs to
|
||||
Assume the call @findCabalHelperEntryPoint "\/Foo\/B\/src\/Lib2.hs"@.
|
||||
We now want to know to which project "\/Foo\/B\/src\/Lib2.hs" belongs to
|
||||
and what the projects root is. If we only do a naive search to find the
|
||||
first occurrence of either "B.cabal", "stack.yaml", "cabal.project"
|
||||
or "Foo.cabal", we might assume that the location of "B.cabal" marks
|
||||
the project's root directory of which "/Foo/B/src/Lib2.hs" is part of.
|
||||
the project's root directory of which "\/Foo\/B\/src\/Lib2.hs" is part of.
|
||||
However, there is also a "cabal.project" and "stack.yaml" in the parent
|
||||
directory, which add the package "B" as a package.
|
||||
So, the compilation of the package "B", and the file "src/Lib2.hs" in it,
|
||||
directory, which add the package @B@ as a package.
|
||||
So, the compilation of the package @B@, and the file "src\/Lib2.hs" in it,
|
||||
does not only depend on the definitions in "B.cabal", but also
|
||||
on "stack.yaml" and "cabal.project".
|
||||
The project root is therefore "/Foo/".
|
||||
The project root is therefore "\/Foo\/".
|
||||
Only if there is no "stack.yaml" or "cabal.project" in any of the ancestor
|
||||
directories, it is safe to assume that "B.cabal" marks the root of the project.
|
||||
|
||||
Thus:
|
||||
|
||||
>>> findCabalHelperEntryPoint "/Foo/B/src/Lib2.hs
|
||||
Just (Ex (ProjLocStackYaml { plStackYaml = "/Foo/"}))
|
||||
|
||||
or
|
||||
>>> findCabalHelperEntryPoint "/Foo/B/src/Lib2.hs
|
||||
|
||||
>>> findCabalHelperEntryPoint "/Foo/B/src/Lib2.hs"
|
||||
Just (Ex (ProjLocV2File { plProjectDirV2 = "/Foo/"}))
|
||||
|
||||
In the given example, it is not guaranteed which project type is found,
|
||||
it is only guaranteed that it will not identify the project
|
||||
as a cabal v1-project.
|
||||
as a cabal v1-project. Note that with cabal-helper version (1.0),
|
||||
by default a *.cabal file is identified as a 'ProjLocV2Dir' project.
|
||||
The same issue as before exists and we look for a 'ProjLocV2File' or
|
||||
'ProjLocStackYaml' before deciding that 'ProjLocV2Dir' marks the project root.
|
||||
|
||||
Note that this will not return any project types for which the corresponding
|
||||
build tool is not on the PATH. This is "stack" and "cabal" for stack and cabal
|
||||
@ -219,29 +232,33 @@ findCabalHelperEntryPoint fp = do
|
||||
let supportedProjs = filter (\x -> supported x isStackInstalled isCabalInstalled) allProjs
|
||||
debugm $ "These projects have the build tools installed: " ++ show (map (\(Ex x) -> show x) supportedProjs)
|
||||
|
||||
case filter (\p -> isCabalNewProject p || isStackProject p) supportedProjs of
|
||||
case filter (\p -> isCabalV2FileProject p || isStackProject p) supportedProjs of
|
||||
(x:_) -> return $ Just x
|
||||
[] -> case filter isCabalOldProject supportedProjs of
|
||||
[] -> case filter isCabalProject supportedProjs of
|
||||
(x:_) -> return $ Just x
|
||||
[] -> return Nothing
|
||||
where
|
||||
supported :: (Ex ProjLoc) -> Bool -> Bool -> Bool
|
||||
supported :: Ex ProjLoc -> Bool -> Bool -> Bool
|
||||
supported (Ex ProjLocStackYaml {}) stackInstalled _ = stackInstalled
|
||||
supported (Ex ProjLocV2Dir {}) _ cabalInstalled = cabalInstalled
|
||||
supported (Ex ProjLocV2File {}) _ cabalInstalled = cabalInstalled
|
||||
supported (Ex ProjLocV1Dir {}) _ cabalInstalled = cabalInstalled
|
||||
supported (Ex ProjLocV1CabalFile {}) _ cabalInstalled = cabalInstalled
|
||||
|
||||
isStackProject (Ex ProjLocStackYaml {}) = True
|
||||
isStackProject _ = False
|
||||
isStackProject :: Ex ProjLoc -> Bool
|
||||
isStackProject (Ex ProjLocStackYaml {}) = True
|
||||
isStackProject _ = False
|
||||
|
||||
isCabalNewProject (Ex ProjLocV2Dir {}) = True
|
||||
isCabalNewProject (Ex ProjLocV2File {}) = True
|
||||
isCabalNewProject _ = False
|
||||
isCabalV2FileProject :: Ex ProjLoc -> Bool
|
||||
isCabalV2FileProject (Ex ProjLocV2File {}) = True
|
||||
isCabalV2FileProject _ = False
|
||||
|
||||
isCabalOldProject (Ex ProjLocV1Dir {}) = True
|
||||
isCabalOldProject (Ex ProjLocV1CabalFile {}) = True
|
||||
isCabalOldProject _ = False
|
||||
isCabalProject :: Ex ProjLoc -> Bool
|
||||
isCabalProject (Ex ProjLocV1CabalFile {}) = True
|
||||
isCabalProject (Ex ProjLocV1Dir {}) = True
|
||||
isCabalProject (Ex ProjLocV2File {}) = True
|
||||
isCabalProject (Ex ProjLocV2Dir {}) = True
|
||||
isCabalProject _ = False
|
||||
|
||||
{- | Given a FilePath, find the cradle the FilePath belongs to.
|
||||
|
||||
@ -330,6 +347,8 @@ required to load and compile the given file.
|
||||
=== Mono-Repo
|
||||
|
||||
Assume the project structure:
|
||||
|
||||
@
|
||||
/
|
||||
└── Mono/
|
||||
├── cabal.project
|
||||
@ -340,6 +359,7 @@ Assume the project structure:
|
||||
└── B/
|
||||
├── B.cabal
|
||||
└── Exe.hs
|
||||
@
|
||||
|
||||
Currently, Haskell IDE Engine needs to know on startup which GHC version is
|
||||
needed to compile the project. This information is needed to show warnings to
|
||||
@ -347,14 +367,16 @@ the user if the GHC version on the project does not agree with the GHC version
|
||||
that was used to compile Haskell IDE Engine.
|
||||
|
||||
Therefore, the function 'findLocalCradle' is invoked with a dummy FilePath,
|
||||
such as "/Mono/Lib.hs". Since there will be no package that contains this
|
||||
such as "\/Mono\/Lib.hs". Since there will be no package that contains this
|
||||
dummy FilePath, the result will be a None-cradle.
|
||||
|
||||
Either
|
||||
|
||||
>>> findLocalCradle "/Mono/Lib.hs"
|
||||
Cradle { cradleRootDir = "/Mono/", CradleAction { actionName = "Cabal-Helper-Stack-None", ..} }
|
||||
|
||||
or:
|
||||
or
|
||||
|
||||
>>> findLocalCradle "/Mono/Lib.hs"
|
||||
Cradle { cradleRootDir = "/Mono/", CradleAction { actionName = "Cabal-Helper-Cabal-V2-None", ..} }
|
||||
|
||||
@ -364,8 +386,9 @@ a 'cabal' project.
|
||||
|
||||
|
||||
If we are trying to load the executable:
|
||||
|
||||
>>> findLocalCradle "/Mono/B/Exe.hs"
|
||||
Cradle { cradleRootDir = "/Mono/B/", CradleAction { actionName = "Cabal-Helper-Cabal-V2", ..} }
|
||||
Cradle { cradleRootDir = "/Mono/", CradleAction { actionName = "Cabal-Helper-Cabal-V2", ..} }
|
||||
|
||||
we will detect correctly the compiler options, by first finding the appropriate
|
||||
package, followed by traversing the units in the package and finding the
|
||||
@ -374,6 +397,8 @@ component that exposes the executable by FilePath.
|
||||
=== No explicit executable folder
|
||||
|
||||
Assume the project structure:
|
||||
|
||||
@
|
||||
/
|
||||
└── Library/
|
||||
├── cabal.project
|
||||
@ -382,18 +407,21 @@ Assume the project structure:
|
||||
└── src
|
||||
├── Lib.hs
|
||||
└── Exe.hs
|
||||
@
|
||||
|
||||
There are different dependencies for the library "Lib.hs" and the
|
||||
executable "Exe.hs". If we are trying to load the executable "src/Exe.hs"
|
||||
executable "Exe.hs". If we are trying to load the executable "src\/Exe.hs"
|
||||
we will correctly identify the executable unit, and correctly initialise
|
||||
dependencies of "exe:Library".
|
||||
It will be correct even if we load the unit "lib:Library" before
|
||||
the "exe:Library" because the unit "lib:Library" does not expose
|
||||
a module "Exe".
|
||||
a module @"Exe"@.
|
||||
|
||||
=== Sub package
|
||||
|
||||
Assume the project structure:
|
||||
|
||||
@
|
||||
/
|
||||
└── Repo/
|
||||
├── cabal.project
|
||||
@ -404,12 +432,13 @@ Assume the project structure:
|
||||
└── SubRepo
|
||||
├── SubRepo.cabal
|
||||
└── Lib2.hs
|
||||
@
|
||||
|
||||
When we try to load "/Repo/SubRepo/Lib2.hs", we need to identify root
|
||||
of the project, which is "/Repo/" but set the root directory of the cradle
|
||||
responsible to load "/Repo/SubRepo/Lib2.hs" to "/Repo/SubRepo", since
|
||||
When we try to load "\/Repo\/SubRepo\/Lib2.hs", we need to identify root
|
||||
of the project, which is "\/Repo\/" but set the root directory of the cradle
|
||||
responsible to load "\/Repo\/SubRepo\/Lib2.hs" to "\/Repo\/SubRepo", since
|
||||
the compiler options obtained from Cabal-Helper are relative to the package
|
||||
source directory, which is "/Repo/SubRepo".
|
||||
source directory, which is "\/Repo\/SubRepo".
|
||||
|
||||
-}
|
||||
cabalHelperCradle :: FilePath -> IO Cradle
|
||||
@ -422,17 +451,24 @@ cabalHelperCradle file = do
|
||||
return
|
||||
Cradle { cradleRootDir = cwd
|
||||
, cradleOptsProg =
|
||||
CradleAction { actionName = "Cabal-Helper-None"
|
||||
, runCradle = \_ _ -> return CradleNone
|
||||
CradleAction { actionName = "Direct"
|
||||
, runCradle = \_ _ ->
|
||||
return
|
||||
$ CradleSuccess
|
||||
ComponentOptions
|
||||
{ componentOptions = [file, fixImportDirs cwd "-i."]
|
||||
, componentDependencies = []
|
||||
}
|
||||
}
|
||||
}
|
||||
Just (Ex proj) -> do
|
||||
logm $ "Cabal-Helper decided to use: " ++ show proj
|
||||
-- Find the root of the project based on project type.
|
||||
let root = projectRootDir proj
|
||||
-- Create a suffix for the cradle name.
|
||||
-- Purpose is mainly for easier debugging.
|
||||
let actionNameSuffix = projectSuffix proj
|
||||
logm $ "Cabal-Helper dirs: " ++ show [root, file]
|
||||
debugm $ "Cabal-Helper dirs: " ++ show [root, file]
|
||||
let dist_dir = getDefaultDistDir proj
|
||||
env <- mkQueryEnv proj dist_dir
|
||||
packages <- runQuery projectPackages env
|
||||
@ -468,63 +504,67 @@ cabalHelperCradle file = do
|
||||
CradleAction { actionName =
|
||||
"Cabal-Helper-" ++ actionNameSuffix
|
||||
, runCradle = \_ fp -> cabalHelperAction
|
||||
(Ex proj)
|
||||
env
|
||||
realPackage
|
||||
normalisedPackageLocation
|
||||
fp
|
||||
}
|
||||
}
|
||||
where
|
||||
|
||||
-- | Fix occurrences of "-i." to "-i<cradle-root-dir>"
|
||||
-- Flags obtained from cabal-helper are relative to the package
|
||||
-- source directory. This is less resilient to using absolute paths,
|
||||
-- thus, we fix it here.
|
||||
fixImportDirs :: FilePath -> String -> String
|
||||
fixImportDirs base_dir arg =
|
||||
if "-i" `isPrefixOf` arg
|
||||
then let dir = drop 2 arg
|
||||
-- the flag "-i" has special meaning.
|
||||
in if not (null dir) && isRelative dir then ("-i" ++ base_dir </> dir)
|
||||
else arg
|
||||
else arg
|
||||
-- | Cradle Action to query for the ComponentOptions that are needed
|
||||
-- to load the given FilePath.
|
||||
-- This Function is not supposed to throw any exceptions and use
|
||||
-- 'CradleLoadResult' to indicate errors.
|
||||
cabalHelperAction :: Ex ProjLoc -- ^ Project location, can be used
|
||||
-- to present build-tool
|
||||
-- agnostic error messages.
|
||||
-> QueryEnv v -- ^ Query Env created by 'mkQueryEnv'
|
||||
-- with the appropriate 'distdir'
|
||||
-> Package v -- ^ Package this cradle is part for.
|
||||
-> FilePath -- ^ Root directory of the cradle
|
||||
-- this action belongs to.
|
||||
-> FilePath -- ^ FilePath to load, expected to be an absolute path.
|
||||
-> IO (CradleLoadResult ComponentOptions)
|
||||
cabalHelperAction proj env package root fp = do
|
||||
-- Get all unit infos the given FilePath may belong to
|
||||
let units = pUnits package
|
||||
-- make the FilePath to load relative to the root of the cradle.
|
||||
let relativeFp = makeRelative root fp
|
||||
debugm $ "Relative Module FilePath: " ++ relativeFp
|
||||
getComponent proj env (toList units) relativeFp
|
||||
>>= \case
|
||||
Right comp -> do
|
||||
let fs' = getFlags comp
|
||||
let fs = map (fixImportDirs root) fs'
|
||||
let targets = getTargets comp relativeFp
|
||||
let ghcOptions = fs ++ targets
|
||||
debugm $ "Flags for \"" ++ fp ++ "\": " ++ show ghcOptions
|
||||
debugm $ "Component Infos: " ++ show comp
|
||||
return
|
||||
$ CradleSuccess
|
||||
ComponentOptions { componentOptions = ghcOptions
|
||||
, componentDependencies = []
|
||||
}
|
||||
Left err -> return
|
||||
$ CradleFail
|
||||
$ CradleError
|
||||
(ExitFailure 2)
|
||||
err
|
||||
|
||||
-- | Fix occurrences of "-i." to "-i<cradle-root-dir>"
|
||||
-- Flags obtained from cabal-helper are relative to the package
|
||||
-- source directory. This is less resilient to using absolute paths,
|
||||
-- thus, we fix it here.
|
||||
fixImportDirs :: FilePath -> String -> String
|
||||
fixImportDirs base_dir arg =
|
||||
if "-i" `isPrefixOf` arg
|
||||
then let dir = drop 2 arg
|
||||
-- the flag "-i" has special meaning.
|
||||
in if not (null dir) && isRelative dir then ("-i" ++ base_dir </> dir)
|
||||
else arg
|
||||
else arg
|
||||
|
||||
-- | cradle Action to query for the ComponentOptions that are needed
|
||||
-- to load the given FilePath.
|
||||
-- This Function is not supposed to throw any exceptions and use
|
||||
-- 'CradleLoadResult' to indicate errors.
|
||||
cabalHelperAction :: QueryEnv v -- ^ Query Env created by 'mkQueryEnv'
|
||||
-- with the appropriate 'distdir'
|
||||
-> Package v -- ^ Package this cradle is part for.
|
||||
-> FilePath -- ^ Root directory of the cradle
|
||||
-- this action belongs to.
|
||||
-> FilePath -- ^ FilePath to load, expected to be an absolute path.
|
||||
-> IO (CradleLoadResult ComponentOptions)
|
||||
cabalHelperAction env package root fp = do
|
||||
-- Get all unit infos the given FilePath may belong to
|
||||
let units = pUnits package
|
||||
-- make the FilePath to load relative to the root of the cradle.
|
||||
let relativeFp = makeRelative root fp
|
||||
debugm $ "Relative Module FilePath: " ++ relativeFp
|
||||
getComponent env (toList units) relativeFp
|
||||
>>= \case
|
||||
Just comp -> do
|
||||
let fs' = getFlags comp
|
||||
let fs = map (fixImportDirs root) fs'
|
||||
let targets = getTargets comp relativeFp
|
||||
let ghcOptions = fs ++ targets
|
||||
debugm $ "Flags for \"" ++ fp ++ "\": " ++ show ghcOptions
|
||||
debugm $ "Component Infos: " ++ show comp
|
||||
return
|
||||
$ CradleSuccess
|
||||
ComponentOptions { componentOptions = ghcOptions
|
||||
, componentDependencies = []
|
||||
}
|
||||
Nothing -> return
|
||||
$ CradleFail
|
||||
$ CradleError
|
||||
(ExitFailure 2)
|
||||
["Could not obtain flags for " ++ fp]
|
||||
|
||||
-- | Get the component the given FilePath most likely belongs to.
|
||||
-- Lazily ask units whether the given FilePath is part of one of their
|
||||
@ -534,25 +574,96 @@ cabalHelperCradle file = do
|
||||
-- The given FilePath must be relative to the Root of the project
|
||||
-- the given units belong to.
|
||||
getComponent
|
||||
:: QueryEnv pt -> [Unit pt] -> FilePath -> IO (Maybe ChComponentInfo)
|
||||
getComponent _env [] _fp = return Nothing
|
||||
getComponent env (unit : units) fp =
|
||||
try (runQuery (unitInfo unit) env) >>= \case
|
||||
Left (e :: IOException) -> do
|
||||
warningm $ "Catching and swallowing an IOException: " ++ show e
|
||||
warningm
|
||||
$ "The Exception was thrown in the context of finding"
|
||||
++ " a component for \""
|
||||
++ fp
|
||||
++ "\" in the unit: "
|
||||
++ show unit
|
||||
getComponent env units fp
|
||||
Right ui -> do
|
||||
let components = M.elems (uiComponents ui)
|
||||
debugm $ "Unit Info: " ++ show ui
|
||||
case find (fp `partOfComponent`) components of
|
||||
Nothing -> getComponent env units fp
|
||||
comp -> return comp
|
||||
:: forall pt. Ex ProjLoc -> QueryEnv pt -> [Unit pt] -> FilePath -> IO (Either [String] ChComponentInfo)
|
||||
getComponent proj env unitCandidates fp = getComponent' [] [] unitCandidates >>=
|
||||
\case
|
||||
(tried, failed, Nothing) -> return (Left $ buildErrorMsg tried failed)
|
||||
(_, _, Just comp) -> return (Right comp)
|
||||
where
|
||||
getComponent' :: [UnitInfo] -> [(Unit pt, IOException)] -> [Unit pt] -> IO ([UnitInfo], [(Unit pt, IOException)], Maybe ChComponentInfo)
|
||||
getComponent' triedUnits failedUnits [] = return (triedUnits, failedUnits, Nothing)
|
||||
getComponent' triedUnits failedUnits (unit : units) =
|
||||
try (runQuery (unitInfo unit) env) >>= \case
|
||||
Left (e :: IOException) -> do
|
||||
warningm $ "Catching and swallowing an IOException: " ++ show e
|
||||
warningm
|
||||
$ "The Exception was thrown in the context of finding"
|
||||
++ " a component for \""
|
||||
++ fp
|
||||
++ "\" in the unit: "
|
||||
++ show unit
|
||||
getComponent' triedUnits ((unit, e):failedUnits) units
|
||||
Right ui -> do
|
||||
let components = Map.elems (uiComponents ui)
|
||||
debugm $ "Unit Info: " ++ show ui
|
||||
case find (fp `partOfComponent`) components of
|
||||
Nothing -> getComponent' (ui:triedUnits) failedUnits units
|
||||
comp -> return (triedUnits, failedUnits, comp)
|
||||
|
||||
buildErrorMsg :: [UnitInfo] -> [(Unit pt, IOException)] -> [String]
|
||||
buildErrorMsg triedUnits failedUnits =
|
||||
concat
|
||||
[ [ "Could not obtain flags for: \"" ++ fp ++ "\"."
|
||||
, ""
|
||||
]
|
||||
, concat
|
||||
[ concat
|
||||
[ [ "This module was not part of any component we are aware of."
|
||||
, ""
|
||||
]
|
||||
, concatMap ppShowUnitInfo triedUnits
|
||||
, [ ""
|
||||
, ""
|
||||
]
|
||||
, if isStackProject proj
|
||||
then stackSpecificInstructions
|
||||
else cabalSpecificInstructions
|
||||
]
|
||||
| not (null triedUnits)
|
||||
]
|
||||
, concat
|
||||
[
|
||||
[ "We could not build all components."
|
||||
, "If one of these components exposes this Module, make sure they compile."
|
||||
, "You can try to invoke the commands yourself."
|
||||
, "The following commands failed:"
|
||||
]
|
||||
++ concatMap (ppShowIOException . snd) failedUnits
|
||||
| not (null failedUnits)
|
||||
]
|
||||
]
|
||||
|
||||
stackSpecificInstructions :: [String]
|
||||
stackSpecificInstructions =
|
||||
[ "To expose a module, refer to:"
|
||||
, "https://docs.haskellstack.org/en/stable/GUIDE/"
|
||||
, "If you are using `package.yaml` then you don't have to manually expose modules."
|
||||
, "Maybe you didn't set the source directories for your project correctly."
|
||||
]
|
||||
|
||||
cabalSpecificInstructions :: [String]
|
||||
cabalSpecificInstructions =
|
||||
[ "To expose a module, refer to:"
|
||||
, "https://www.haskell.org/cabal/users-guide/developing-packages.html"
|
||||
, ""
|
||||
]
|
||||
|
||||
ppShowUnitInfo :: UnitInfo -> [String]
|
||||
ppShowUnitInfo u =
|
||||
u
|
||||
& uiComponents
|
||||
& Map.toList
|
||||
& map
|
||||
(\(name, info) ->
|
||||
"Component: " ++ show name ++ " with source directory: " ++ show (ciSourceDirs info)
|
||||
)
|
||||
|
||||
|
||||
ppShowIOException :: IOException -> [String]
|
||||
ppShowIOException e =
|
||||
[ ""
|
||||
, show e
|
||||
]
|
||||
|
||||
-- | Check whether the given FilePath is part of the Component.
|
||||
-- A FilePath is part of the Component if and only if:
|
||||
@ -734,8 +845,8 @@ ancestors dir
|
||||
where
|
||||
subdir = takeDirectory dir
|
||||
|
||||
-- | Assuming a FilePath "src/Lib/Lib.hs" and a list of directories
|
||||
-- such as ["src", "app"], returns either the given FilePath
|
||||
-- | Assuming a FilePath @"src\/Lib\/Lib.hs"@ and a list of directories
|
||||
-- such as @["src", "app"]@, returns either the given FilePath
|
||||
-- with a matching directory stripped away.
|
||||
-- If there are multiple matches, e.g. multiple directories are a prefix
|
||||
-- of the given FilePath, return the first match in the list.
|
||||
@ -767,4 +878,3 @@ cradleDisplay cradle = fromString result
|
||||
| "multi" `isInfixOf` name = "Multi Component project"
|
||||
| otherwise = "project"
|
||||
name = map toLower $ BIOS.actionName (BIOS.cradleOptsProg cradle)
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||
{-# LANGUAGE FlexibleInstances #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
{-# LANGUAGE TupleSections #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
@ -24,6 +23,7 @@ module Haskell.Ide.Engine.ModuleCache
|
||||
, cacheInfoNoClear
|
||||
, runActionWithContext
|
||||
, ModuleCache(..)
|
||||
, PublishDiagnostics
|
||||
) where
|
||||
|
||||
|
||||
@ -32,26 +32,28 @@ import Control.Monad
|
||||
import Control.Monad.IO.Class
|
||||
import Control.Monad.Trans.Control
|
||||
import Control.Monad.Trans.Free
|
||||
import qualified Data.Aeson as Aeson
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
import Data.Dynamic (toDyn, fromDynamic, Dynamic)
|
||||
import Data.Generics (Proxy(..), TypeRep, typeRep, typeOf)
|
||||
import qualified Data.Map as Map
|
||||
import Data.Maybe
|
||||
import qualified Data.SortedList as SL
|
||||
import qualified Data.Trie.Convenience as T
|
||||
import qualified Data.Trie as T
|
||||
import qualified Data.Text as Text
|
||||
import Data.Typeable (Typeable)
|
||||
import qualified Data.Yaml as Yaml
|
||||
import System.Directory
|
||||
|
||||
|
||||
import qualified GHC
|
||||
import qualified HscMain as GHC
|
||||
import qualified HIE.Bios as Bios
|
||||
import qualified HIE.Bios.Ghc.Api as Bios
|
||||
|
||||
import qualified Data.Aeson as Aeson
|
||||
import qualified Data.Trie.Convenience as T
|
||||
import qualified Data.Trie as T
|
||||
import qualified Data.Text as Text
|
||||
import qualified Data.Yaml as Yaml
|
||||
import qualified HIE.Bios as BIOS
|
||||
import qualified HIE.Bios.Ghc.Api as BIOS
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
|
||||
import qualified Language.Haskell.LSP.Types as J
|
||||
import qualified Language.Haskell.LSP.Diagnostics as J
|
||||
import Haskell.Ide.Engine.ArtifactMap
|
||||
import Haskell.Ide.Engine.Cradle (findLocalCradle, cradleDisplay)
|
||||
import Haskell.Ide.Engine.TypeMap
|
||||
@ -68,6 +70,9 @@ modifyCache :: (HasGhcModuleCache m) => (GhcModuleCache -> GhcModuleCache) -> m
|
||||
modifyCache f = modifyModuleCache f
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
type PublishDiagnostics = J.NormalizedUri -> J.TextDocumentVersion -> J.DiagnosticsBySource -> IO ()
|
||||
|
||||
-- | Run the given action in context and initialise a session with hie-bios.
|
||||
-- If a context is given, the context is used to initialise a session for GHC.
|
||||
-- The project "hie-bios" is used to find a Cradle and setup a GHC session
|
||||
@ -88,22 +93,23 @@ modifyCache f = modifyModuleCache f
|
||||
-- though we know nothing about the file.
|
||||
-- 2. Return the default value for the specific action.
|
||||
runActionWithContext :: (MonadIde m, GHC.GhcMonad m, HasGhcModuleCache m, MonadBaseControl IO m)
|
||||
=> GHC.DynFlags
|
||||
=> PublishDiagnostics
|
||||
-> GHC.DynFlags
|
||||
-> Maybe FilePath -- ^ Context for the Action
|
||||
-> a -- ^ Default value for none cradle
|
||||
-> m a -- ^ Action to execute
|
||||
-> m (IdeResult a) -- ^ Result of the action or error in
|
||||
-- the context initialisation.
|
||||
runActionWithContext _df Nothing _def action =
|
||||
runActionWithContext _pub _df Nothing _def action =
|
||||
-- Cradle with no additional flags
|
||||
-- dir <- liftIO $ getCurrentDirectory
|
||||
--This causes problems when loading a later package which sets the
|
||||
--packageDb
|
||||
-- loadCradle df (BIOS.defaultCradle dir)
|
||||
-- loadCradle df (Bios.defaultCradle dir)
|
||||
fmap IdeResultOk action
|
||||
runActionWithContext df (Just uri) def action = do
|
||||
runActionWithContext publishDiagnostics df (Just uri) def action = do
|
||||
mcradle <- getCradle uri
|
||||
loadCradle df mcradle def action
|
||||
loadCradle publishDiagnostics df mcradle def action
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
@ -114,17 +120,18 @@ runActionWithContext df (Just uri) def action = do
|
||||
-- to set up the Session, including downloading all dependencies of a Cradle.
|
||||
loadCradle :: forall a m . (MonadIde m, HasGhcModuleCache m, GHC.GhcMonad m
|
||||
, MonadBaseControl IO m)
|
||||
=> GHC.DynFlags
|
||||
=> PublishDiagnostics
|
||||
-> GHC.DynFlags
|
||||
-> LookupCradleResult
|
||||
-> a
|
||||
-> m a
|
||||
-> m (IdeResult a)
|
||||
loadCradle _ ReuseCradle _def action = do
|
||||
loadCradle _ _ ReuseCradle _def action = do
|
||||
-- Since we expect this message to show up often, only show in debug mode
|
||||
debugm "Reusing cradle"
|
||||
IdeResultOk <$> action
|
||||
|
||||
loadCradle _iniDynFlags (LoadCradle (CachedCradle crd env)) _def action = do
|
||||
loadCradle _ _iniDynFlags (LoadCradle (CachedCradle crd env)) _def action = do
|
||||
-- Reloading a cradle happens on component switch
|
||||
logm $ "Switch to cradle: " ++ show crd
|
||||
-- Cache the existing cradle
|
||||
@ -133,7 +140,7 @@ loadCradle _iniDynFlags (LoadCradle (CachedCradle crd env)) _def action = do
|
||||
setCurrentCradle crd
|
||||
IdeResultOk <$> action
|
||||
|
||||
loadCradle iniDynFlags (NewCradle fp) def action = do
|
||||
loadCradle publishDiagnostics iniDynFlags (NewCradle fp) def action = do
|
||||
-- If this message shows up a lot in the logs, it is an indicator for a bug
|
||||
logm $ "New cradle: " ++ fp
|
||||
-- Cache the existing cradle
|
||||
@ -156,34 +163,49 @@ loadCradle iniDynFlags (NewCradle fp) def action = do
|
||||
where
|
||||
-- | Initialise the given cradle. This might fail and return an error via `IdeResultFail`.
|
||||
-- Reports its progress to the client.
|
||||
initialiseCradle :: (MonadIde m, HasGhcModuleCache m, GHC.GhcMonad m, MonadBaseControl IO m)
|
||||
=> BIOS.Cradle -> (Progress -> IO ()) -> m (IdeResult a)
|
||||
initialiseCradle :: (MonadIde m, HasGhcModuleCache m, GHC.GhcMonad m)
|
||||
=> Bios.Cradle -> (Progress -> IO ()) -> m (IdeResult a)
|
||||
initialiseCradle cradle f = do
|
||||
res <- BIOS.initializeFlagsWithCradleWithMessage (Just (toMessager f)) fp cradle
|
||||
res <- Bios.initializeFlagsWithCradleWithMessage (Just (toMessager f)) fp cradle
|
||||
case res of
|
||||
BIOS.CradleNone ->
|
||||
Bios.CradleNone ->
|
||||
-- Note: The action is not run if we are in the none cradle, we
|
||||
-- just pretend the file doesn't exist.
|
||||
return $ IdeResultOk def
|
||||
BIOS.CradleFail err -> do
|
||||
logm $ "GhcException on cradle initialisation: " ++ show err
|
||||
Bios.CradleFail (Bios.CradleError code msg) -> do
|
||||
warningm $ "Fail on cradle initialisation: (" ++ show code ++ ")" ++ show msg
|
||||
|
||||
-- Send a detailed diagnostic to the user.
|
||||
|
||||
let normalizedUri = J.toNormalizedUri (filePathToUri fp)
|
||||
sev = Just DsError
|
||||
range = Range (Position 0 0) (Position 1 0)
|
||||
msgTxt =
|
||||
[ "Fail on initialisation for \"" <> Text.pack fp <> "\"."
|
||||
] <> map Text.pack msg
|
||||
source = Just "bios"
|
||||
diag = Diagnostic range sev Nothing source (Text.unlines msgTxt) Nothing
|
||||
|
||||
liftIO $ publishDiagnostics normalizedUri Nothing
|
||||
(Map.singleton source (SL.singleton diag))
|
||||
|
||||
return $ IdeResultFail $ IdeError
|
||||
{ ideCode = OtherError
|
||||
, ideMessage = Text.pack $ show err
|
||||
, ideMessage = Text.unwords (take 2 msgTxt)
|
||||
, ideInfo = Aeson.Null
|
||||
}
|
||||
BIOS.CradleSuccess init_session -> do
|
||||
Bios.CradleSuccess init_session -> do
|
||||
-- Note that init_session contains a Hook to 'f'.
|
||||
-- So, it can still provide Progress Reports.
|
||||
-- Therefore, invocation of 'init_session' must happen
|
||||
-- while 'f' is still valid.
|
||||
liftIO (GHC.newHscEnv iniDynFlags) >>= GHC.setSession
|
||||
liftIO $ setCurrentDirectory (BIOS.cradleRootDir cradle)
|
||||
liftIO $ setCurrentDirectory (Bios.cradleRootDir cradle)
|
||||
|
||||
let onGhcError = return . Left
|
||||
let onSourceError srcErr = do
|
||||
logm $ "Source error on cradle initialisation: " ++ show srcErr
|
||||
return $ Right BIOS.Failed
|
||||
return $ Right Bios.Failed
|
||||
-- We continue setting the cradle in case the file has source errors
|
||||
-- cause they will be reported to user by diagnostics
|
||||
init_res <- gcatches
|
||||
@ -202,12 +224,12 @@ loadCradle iniDynFlags (NewCradle fp) def action = do
|
||||
-- it on a save whilst there are errors. Subsequent loads won't
|
||||
-- be that slow, even though the cradle isn't cached because the
|
||||
-- `.hi` files will be saved.
|
||||
Right BIOS.Succeeded -> do
|
||||
Right Bios.Succeeded -> do
|
||||
setCurrentCradle cradle
|
||||
logm "Cradle set succesfully"
|
||||
IdeResultOk <$> action
|
||||
|
||||
Right BIOS.Failed -> do
|
||||
Right Bios.Failed -> do
|
||||
setCurrentCradle cradle
|
||||
logm "Cradle did not load succesfully"
|
||||
IdeResultOk <$> action
|
||||
@ -217,7 +239,7 @@ loadCradle iniDynFlags (NewCradle fp) def action = do
|
||||
-- that belong to this cradle.
|
||||
-- If the cradle does not load any module, it is responsible for an empty
|
||||
-- list of Modules.
|
||||
setCurrentCradle :: (HasGhcModuleCache m, GHC.GhcMonad m) => BIOS.Cradle -> m ()
|
||||
setCurrentCradle :: (HasGhcModuleCache m, GHC.GhcMonad m) => Bios.Cradle -> m ()
|
||||
setCurrentCradle cradle = do
|
||||
mg <- GHC.getModuleGraph
|
||||
let ps = mapMaybe (GHC.ml_hs_file . GHC.ms_location) (mgModSummaries mg)
|
||||
@ -230,7 +252,7 @@ setCurrentCradle cradle = do
|
||||
-- for.
|
||||
-- Via 'lookupCradle' it can be checked if a given FilePath is managed by
|
||||
-- a any Cradle that has already been loaded.
|
||||
cacheCradle :: (HasGhcModuleCache m, GHC.GhcMonad m) => ([FilePath], BIOS.Cradle) -> m ()
|
||||
cacheCradle :: (HasGhcModuleCache m, GHC.GhcMonad m) => ([FilePath], Bios.Cradle) -> m ()
|
||||
cacheCradle (ds, c) = do
|
||||
env <- GHC.getSession
|
||||
let cc = CachedCradle c env
|
||||
|
@ -1,6 +1,7 @@
|
||||
name: hie-plugin-api
|
||||
version: 0.14.0.0
|
||||
version: 1.0.0.0
|
||||
synopsis: Haskell IDE API for plugin communication
|
||||
description: Please see README.md
|
||||
license: BSD3
|
||||
license-file: LICENSE
|
||||
author: Many,TBD when we release
|
||||
@ -15,6 +16,7 @@ cabal-version: >=2.0
|
||||
flag pedantic
|
||||
Description: Enable -Werror
|
||||
Default: False
|
||||
Manual: True
|
||||
|
||||
library
|
||||
exposed-modules:
|
||||
@ -57,6 +59,7 @@ library
|
||||
, monad-control
|
||||
, mtl
|
||||
, process
|
||||
, sorted-list
|
||||
, stm
|
||||
, syb
|
||||
, text
|
||||
|
35
hie.yaml.cbl
Normal file
35
hie.yaml.cbl
Normal file
@ -0,0 +1,35 @@
|
||||
# This is a sample hie.yaml file for opening haskell-ide-engine in
|
||||
# hie, using cabal as the build system.
|
||||
# To use is, copy it to a file called 'hie.yaml'
|
||||
|
||||
cradle:
|
||||
cabal:
|
||||
- path: "./hie-plugin-api/Haskell"
|
||||
component: "lib:hie-plugin-api"
|
||||
|
||||
- path: "./test/dispatcher/"
|
||||
component: "haskell-ide-engine:dispatcher-test"
|
||||
|
||||
- path: "./test/functional/"
|
||||
component: "haskell-ide-engine:func-test"
|
||||
|
||||
- path: "./test/unit/"
|
||||
component: "haskell-ide-engine:unit-test"
|
||||
|
||||
- path: "./test/plugin-dispatcher/"
|
||||
component: "haskell-ide-engine:plugin-dispatcher-test"
|
||||
|
||||
- path: "./test/wrapper/"
|
||||
component: "haskell-ide-engine:wrapper-test"
|
||||
|
||||
- path: "./test/utils/"
|
||||
component: "haskell-ide-engine:hie-test-utils"
|
||||
|
||||
- path: "./app/MainHie.hs"
|
||||
component: "haskell-ide-engine:hie"
|
||||
|
||||
- path: "./app/HieWrapper.hs"
|
||||
component: "haskell-ide-engine:hie-wrapper"
|
||||
|
||||
- path: "./"
|
||||
component: "lib:haskell-ide-engine"
|
36
hie.yaml.stack
Normal file
36
hie.yaml.stack
Normal file
@ -0,0 +1,36 @@
|
||||
# This is a sample hie.yaml file for opening haskell-ide-engine in
|
||||
# hie, using stack as the build system.
|
||||
# To use is, copy it to a file called 'hie.yaml'
|
||||
|
||||
cradle:
|
||||
stack:
|
||||
- path: "./hie-plugin-api/"
|
||||
component: "hie-plugin-api:lib"
|
||||
|
||||
- path: "./test/dispatcher/"
|
||||
component: "haskell-ide-engine:test:dispatcher-test"
|
||||
|
||||
- path: "./test/functional/"
|
||||
component: "haskell-ide-engine:test:func-test"
|
||||
|
||||
- path: "./test/unit/"
|
||||
component: "haskell-ide-engine:test:unit-test"
|
||||
|
||||
- path: "./test/plugin-dispatcher/"
|
||||
component: "haskell-ide-engine:test:plugin-dispatcher-test"
|
||||
|
||||
- path: "./test/wrapper/"
|
||||
component: "haskell-ide-engine:test:wrapper-test"
|
||||
|
||||
# This target does not currently work (stack 2.1.3)
|
||||
- path: "./test/utils/"
|
||||
component: "haskell-ide-engine:lib:hie-test-utils"
|
||||
|
||||
- path: "./app/MainHie.hs"
|
||||
component: "haskell-ide-engine:exe:hie"
|
||||
|
||||
- path: "./app/HieWrapper.hs"
|
||||
component: "haskell-ide-engine:exe:hie-wrapper"
|
||||
|
||||
- path: "./"
|
||||
component: "haskell-ide-engine:lib"
|
@ -31,6 +31,8 @@ library
|
||||
|
||||
if flag(run-from-stack)
|
||||
cpp-options: -DRUN_FROM_STACK
|
||||
else
|
||||
build-depends: cabal-install-parsers
|
||||
|
||||
flag run-from-stack
|
||||
description: Inform the application that it is run from stack
|
||||
|
@ -1,3 +1,5 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
|
||||
module Cabal where
|
||||
|
||||
import Development.Shake
|
||||
@ -15,16 +17,26 @@ import System.Directory ( findExecutable
|
||||
import Version
|
||||
import Print
|
||||
import Env
|
||||
import Stack
|
||||
import Data.Functor.Identity
|
||||
#if RUN_FROM_STACK
|
||||
import Control.Exception ( throwIO )
|
||||
#else
|
||||
import Cabal.Config
|
||||
#endif
|
||||
|
||||
getInstallDir :: IO FilePath
|
||||
#if RUN_FROM_STACK
|
||||
-- we should never hit this codepath
|
||||
getInstallDir = throwIO $ userError "Stack and cabal should never be mixed"
|
||||
#else
|
||||
getInstallDir = runIdentity . cfgInstallDir <$> readConfig
|
||||
#endif
|
||||
|
||||
execCabal :: CmdResult r => [String] -> Action r
|
||||
execCabal = execCabalWithOriginalPath
|
||||
execCabal = command [] "cabal"
|
||||
|
||||
execCabal_ :: [String] -> Action ()
|
||||
execCabal_ = execCabalWithOriginalPath
|
||||
|
||||
execCabalWithOriginalPath :: CmdResult r => [String] -> Action r
|
||||
execCabalWithOriginalPath = withoutStackCachedBinaries . (command [] "cabal")
|
||||
execCabal_ = command [] "cabal"
|
||||
|
||||
cabalBuildData :: Action ()
|
||||
cabalBuildData = do
|
||||
@ -39,19 +51,9 @@ getGhcPathOfOrThrowError versionNumber =
|
||||
error (ghcVersionNotFoundFailMsg versionNumber)
|
||||
Just p -> return p
|
||||
|
||||
cabalBuildHie :: VersionNumber -> Action ()
|
||||
cabalBuildHie versionNumber = do
|
||||
ghcPath <- getGhcPathOfOrThrowError versionNumber
|
||||
execCabal_
|
||||
[ "v2-build"
|
||||
, "-w", ghcPath
|
||||
, "--write-ghc-environment-files=never"
|
||||
, "--max-backjumps=5000"
|
||||
, "--disable-tests"]
|
||||
|
||||
cabalInstallHie :: VersionNumber -> Action ()
|
||||
cabalInstallHie versionNumber = do
|
||||
localBin <- getLocalBin
|
||||
localBin <- liftIO $ getInstallDir
|
||||
cabalVersion <- getCabalVersion
|
||||
ghcPath <- getGhcPathOfOrThrowError versionNumber
|
||||
|
||||
@ -65,6 +67,7 @@ cabalInstallHie versionNumber = do
|
||||
, "-w", ghcPath
|
||||
, "--write-ghc-environment-files=never"
|
||||
, installDirOpt, localBin
|
||||
, "--max-backjumps=5000"
|
||||
, "exe:hie"
|
||||
, "--overwrite-policy=always"
|
||||
]
|
||||
@ -84,18 +87,6 @@ cabalInstallHie versionNumber = do
|
||||
++ minorVerExe
|
||||
++ " to " ++ localBin
|
||||
|
||||
installCabalWithStack :: Action ()
|
||||
installCabalWithStack = do
|
||||
-- try to find existing `cabal` executable with appropriate version
|
||||
mbc <- withoutStackCachedBinaries (liftIO (findExecutable "cabal"))
|
||||
|
||||
case mbc of
|
||||
Just c -> do
|
||||
cabalVersion <- checkCabal
|
||||
printLine $ "There is already a cabal executable in $PATH with the required minimum version: " ++ cabalVersion
|
||||
-- install `cabal-install` if not already installed
|
||||
Nothing -> execStackShake_ ["install", "cabal-install"]
|
||||
|
||||
checkCabal_ :: Action ()
|
||||
checkCabal_ = checkCabal >> return ()
|
||||
|
||||
|
@ -78,8 +78,7 @@ helpMessage versions@BuildableVersions {..} = do
|
||||
[emptyTarget]
|
||||
[ generalTargets
|
||||
, defaultTargets
|
||||
, stackTargets
|
||||
, cabalTargets
|
||||
, if isRunFromCabal then [cabalGhcsTarget] else []
|
||||
, [macosIcuTarget]
|
||||
]
|
||||
|
||||
@ -89,22 +88,6 @@ helpMessage versions@BuildableVersions {..} = do
|
||||
defaultTargets = [buildTarget, buildLatestTarget, buildDataTarget]
|
||||
++ map hieTarget (getDefaultBuildSystemVersions versions)
|
||||
|
||||
stackTargets =
|
||||
[ stackTarget buildTarget
|
||||
, stackTarget buildLatestTarget
|
||||
, stackTarget buildDataTarget
|
||||
]
|
||||
++ (if isRunFromStack then [stackTarget installCabalTarget] else [])
|
||||
++ map (stackTarget . hieTarget) stackVersions
|
||||
|
||||
cabalTargets =
|
||||
[ cabalGhcsTarget
|
||||
, cabalTarget buildTarget
|
||||
, cabalTarget buildLatestTarget
|
||||
, cabalTarget buildDataTarget
|
||||
]
|
||||
++ map (cabalTarget . hieTarget) cabalVersions
|
||||
|
||||
-- | Empty target. Purpose is to introduce a newline between the targets
|
||||
emptyTarget :: (String, String)
|
||||
emptyTarget = ("", "")
|
||||
@ -112,29 +95,19 @@ emptyTarget = ("", "")
|
||||
templateTarget :: (String, String)
|
||||
templateTarget = ("<target>", "")
|
||||
|
||||
targetWithBuildSystem :: String -> TargetDescription -> TargetDescription
|
||||
targetWithBuildSystem system (target, description) =
|
||||
(system ++ "-" ++ target, description ++ "; with " ++ system)
|
||||
|
||||
stackTarget :: TargetDescription -> TargetDescription
|
||||
stackTarget = targetWithBuildSystem "stack"
|
||||
|
||||
cabalTarget :: TargetDescription -> TargetDescription
|
||||
cabalTarget = targetWithBuildSystem "cabal"
|
||||
|
||||
hieTarget :: String -> TargetDescription
|
||||
hieTarget version =
|
||||
("hie-" ++ version, "Builds hie for GHC version " ++ version)
|
||||
|
||||
buildTarget :: TargetDescription
|
||||
buildTarget = ("build", "Build hie with the latest available GHC and the data files")
|
||||
buildTarget = ("hie", "Build hie with the latest available GHC and the data files")
|
||||
|
||||
buildLatestTarget :: TargetDescription
|
||||
buildLatestTarget = ("build-latest", "Build hie with the latest available GHC")
|
||||
buildLatestTarget = ("latest", "Build hie with the latest available GHC")
|
||||
|
||||
buildDataTarget :: TargetDescription
|
||||
buildDataTarget =
|
||||
("build-data", "Get the required data-files for `hie` (Hoogle DB)")
|
||||
("data", "Get the required data-files for `hie` (Hoogle DB)")
|
||||
|
||||
-- special targets
|
||||
|
||||
@ -146,25 +119,6 @@ helpTarget = ("help", "Show help message including all targets")
|
||||
|
||||
cabalGhcsTarget :: TargetDescription
|
||||
cabalGhcsTarget =
|
||||
( "cabal-ghcs"
|
||||
( "ghcs"
|
||||
, "Show all GHC versions that can be installed via `cabal-build`."
|
||||
)
|
||||
|
||||
installCabalTarget :: TargetDescription
|
||||
installCabalTarget =
|
||||
( "install-cabal"
|
||||
, "Install the cabal executable. It will install the required minimum version for hie (currently "
|
||||
++ versionToString requiredCabalVersion
|
||||
++ ") if it isn't already present in $PATH"
|
||||
)
|
||||
|
||||
-- | Creates a message of the form "a, b, c and d", where a,b,c,d are GHC versions.
|
||||
-- If there is no GHC in the list of `hieVersions`
|
||||
allVersionMessage :: [String] -> String
|
||||
allVersionMessage wordList = case wordList of
|
||||
[] -> ""
|
||||
[a] -> show a
|
||||
(a : as) ->
|
||||
let msg = intersperse ", " wordList
|
||||
lastVersion = last msg
|
||||
in concat $ init (init msg) ++ [" and ", lastVersion]
|
||||
|
@ -48,89 +48,64 @@ defaultMain = do
|
||||
|
||||
-- used for cabal-based targets
|
||||
ghcPaths <- findInstalledGhcs
|
||||
let ghcVersions = map fst ghcPaths
|
||||
let cabalVersions = map fst ghcPaths
|
||||
|
||||
-- used for stack-based targets
|
||||
hieVersions <- getHieVersions
|
||||
stackVersions <- getHieVersions
|
||||
|
||||
let versions = BuildableVersions { stackVersions = hieVersions
|
||||
, cabalVersions = ghcVersions
|
||||
}
|
||||
let versions = if isRunFromStack then stackVersions else cabalVersions
|
||||
|
||||
let latestVersion = last hieVersions
|
||||
let toolsVersions = BuildableVersions stackVersions cabalVersions
|
||||
|
||||
putStrLn $ "run from: " ++ buildSystem
|
||||
let latestVersion = last versions
|
||||
|
||||
shakeArgs shakeOptions { shakeFiles = "_build" } $ do
|
||||
want ["short-help"]
|
||||
-- general purpose targets
|
||||
phony "submodules" updateSubmodules
|
||||
phony "cabal" installCabalWithStack
|
||||
phony "short-help" shortHelpMessage
|
||||
phony "all" shortHelpMessage
|
||||
phony "help" (helpMessage versions)
|
||||
phony "check-stack" checkStack
|
||||
phony "check-cabal" checkCabal_
|
||||
|
||||
phony "cabal-ghcs" $ do
|
||||
let
|
||||
msg =
|
||||
"Found the following GHC paths: \n"
|
||||
++ unlines
|
||||
(map (\(version, path) -> "ghc-" ++ version ++ ": " ++ path)
|
||||
ghcPaths
|
||||
)
|
||||
printInStars msg
|
||||
|
||||
-- default-targets
|
||||
phony "build" $ need [buildSystem ++ "-build"]
|
||||
phony "build-latest" $ need [buildSystem ++ "-build-latest"]
|
||||
phony "build-data" $ need [buildSystem ++ "-build-data"]
|
||||
forM_
|
||||
(getDefaultBuildSystemVersions versions)
|
||||
(\version ->
|
||||
phony ("hie-" ++ version) $ need [buildSystem ++ "-hie-" ++ version]
|
||||
)
|
||||
|
||||
-- stack specific targets
|
||||
when isRunFromStack (phony "stack-install-cabal" (need ["cabal"]))
|
||||
phony "stack-build-latest" (need ["stack-hie-" ++ last hieVersions])
|
||||
phony "stack-build" (need ["build-data", "stack-build-latest"])
|
||||
phony "help" (helpMessage toolsVersions)
|
||||
|
||||
phony "stack-build-data" $ do
|
||||
phony "check" (if isRunFromStack then checkStack else checkCabal_)
|
||||
|
||||
phony "data" $ do
|
||||
need ["submodules"]
|
||||
need ["check-stack"]
|
||||
stackBuildData
|
||||
need ["check"]
|
||||
if isRunFromStack then stackBuildData else cabalBuildData
|
||||
|
||||
forM_
|
||||
hieVersions
|
||||
(\version -> phony ("stack-hie-" ++ version) $ do
|
||||
versions
|
||||
(\version -> phony ("hie-" ++ version) $ do
|
||||
need ["submodules"]
|
||||
need ["check-stack"]
|
||||
stackBuildHie version
|
||||
stackInstallHie version
|
||||
need ["check"]
|
||||
if isRunFromStack then do
|
||||
stackBuildHie version
|
||||
stackInstallHie version
|
||||
else
|
||||
cabalInstallHie version
|
||||
)
|
||||
|
||||
phony "latest" (need ["hie-" ++ latestVersion])
|
||||
phony "hie" (need ["data", "latest"])
|
||||
|
||||
-- cabal specific targets
|
||||
phony "cabal-build-latest" (need ["cabal-hie-" ++ last ghcVersions])
|
||||
phony "cabal-build" (need ["build-data", "cabal-build-latest"])
|
||||
phony "cabal-build-data" $ do
|
||||
need ["submodules"]
|
||||
need ["cabal"]
|
||||
cabalBuildData
|
||||
forM_
|
||||
ghcVersions
|
||||
(\version -> phony ("cabal-hie-" ++ version) $ do
|
||||
need ["submodules"]
|
||||
need ["cabal"]
|
||||
cabalBuildHie version
|
||||
cabalInstallHie version
|
||||
)
|
||||
when isRunFromCabal $ do
|
||||
|
||||
phony "ghcs" $ do
|
||||
let
|
||||
msg =
|
||||
"Found the following GHC paths: \n"
|
||||
++ unlines
|
||||
(map (\(version, path) -> "ghc-" ++ version ++ ": " ++ path)
|
||||
ghcPaths
|
||||
)
|
||||
printInStars msg
|
||||
|
||||
-- macos specific targets
|
||||
phony "icu-macos-fix"
|
||||
(need ["icu-macos-fix-install"] >> need ["icu-macos-fix-build"])
|
||||
phony "icu-macos-fix-install" (command_ [] "brew" ["install", "icu4c"])
|
||||
phony "icu-macos-fix-build" $ mapM_ buildIcuMacosFix hieVersions
|
||||
phony "icu-macos-fix-build" $ mapM_ buildIcuMacosFix versions
|
||||
|
||||
|
||||
buildIcuMacosFix :: VersionNumber -> Action ()
|
||||
|
@ -31,11 +31,6 @@ stackInstallHie versionNumber = do
|
||||
copyFile (localBinDir </> hie)
|
||||
(localBinDir </> "hie-" ++ dropExtension versionNumber <.> exe)
|
||||
|
||||
buildCopyCompilerTool :: VersionNumber -> Action ()
|
||||
buildCopyCompilerTool versionNumber =
|
||||
execStackWithGhc_ versionNumber ["build", "--copy-compiler-tool"]
|
||||
|
||||
|
||||
-- | check `stack` has the required version
|
||||
checkStack :: Action ()
|
||||
checkStack = do
|
||||
@ -101,30 +96,3 @@ stackBuildFailMsg =
|
||||
++ "If this does not work, open an issue at \n"
|
||||
++ "\thttps://github.com/haskell/haskell-ide-engine"
|
||||
|
||||
-- |Run actions without the stack cached binaries
|
||||
withoutStackCachedBinaries :: Action a -> Action a
|
||||
withoutStackCachedBinaries action = do
|
||||
|
||||
mbPath <- liftIO (lookupEnv "PATH")
|
||||
|
||||
case (mbPath, isRunFromStack) of
|
||||
|
||||
(Just paths, True) -> do
|
||||
snapshotDir <- trimmedStdout <$> execStackShake ["path", "--snapshot-install-root"]
|
||||
localInstallDir <- trimmedStdout <$> execStackShake ["path", "--local-install-root"]
|
||||
|
||||
let cacheBinPaths = [snapshotDir </> "bin", localInstallDir </> "bin"]
|
||||
let origPaths = removePathsContaining cacheBinPaths paths
|
||||
|
||||
liftIO (setEnv "PATH" origPaths)
|
||||
a <- action
|
||||
liftIO (setEnv "PATH" paths)
|
||||
return a
|
||||
|
||||
otherwise -> action
|
||||
|
||||
where removePathsContaining strs path =
|
||||
joinPaths (filter (not . containsAny) (splitSearchPath path))
|
||||
where containsAny p = any (`isInfixOf` p) strs
|
||||
|
||||
joinPaths = intercalate [searchPathSeparator]
|
@ -6,11 +6,13 @@ import Options.Applicative.Simple
|
||||
data GlobalOpts = GlobalOpts
|
||||
{ optDebugOn :: Bool
|
||||
, optLogFile :: Maybe String
|
||||
, _optLsp :: Bool -- Kept for a while, to not break legacy clients
|
||||
, optLsp :: Bool
|
||||
, projectRoot :: Maybe String
|
||||
, optBiosVerbose :: Bool
|
||||
, optCaptureFile :: Maybe FilePath
|
||||
, optExamplePlugin :: Bool
|
||||
, optDryRun :: Bool
|
||||
, optFiles :: [FilePath]
|
||||
} deriving (Show)
|
||||
|
||||
globalOptsParser :: Parser GlobalOpts
|
||||
@ -26,9 +28,9 @@ globalOptsParser = GlobalOpts
|
||||
<> metavar "LOGFILE"
|
||||
<> help "File to log to, defaults to stdout"
|
||||
))
|
||||
<*> flag True True
|
||||
<*> flag False True
|
||||
( long "lsp"
|
||||
<> help "Legacy flag, no longer used, to enable LSP mode. Not required.")
|
||||
<> help "Start HIE as an LSP server. Otherwise it dumps debug info to stdout")
|
||||
<*> optional (strOption
|
||||
( long "project-root"
|
||||
<> short 'r'
|
||||
@ -53,3 +55,13 @@ globalOptsParser = GlobalOpts
|
||||
<*> switch
|
||||
( long "example"
|
||||
<> help "Enable Example2 plugin. Useful for developers only")
|
||||
<*> flag False True
|
||||
( long "dry-run"
|
||||
<> help "Perform a dry-run of loading files. Only searches for Haskell source files to load. Does nothing if run as LSP server."
|
||||
)
|
||||
<*> many
|
||||
( argument str
|
||||
( metavar "FILES..."
|
||||
<> help "Directories and Filepaths to load. Does nothing if run as LSP server.")
|
||||
)
|
||||
|
||||
|
@ -330,7 +330,7 @@ codeActionProvider plId docId _ context = IdeResultOk <$> hlintActions
|
||||
mkHlintAction diag@(LSP.Diagnostic (LSP.Range start _) _s (Just (LSP.StringValue code)) (Just "hlint") m _) =
|
||||
Just . codeAction <$> mkLspCommand plId "applyOne" title (Just args)
|
||||
where
|
||||
codeAction cmd = LSP.CodeAction title (Just LSP.CodeActionRefactor) (Just (LSP.List [diag])) Nothing (Just cmd)
|
||||
codeAction cmd = LSP.CodeAction title (Just LSP.CodeActionQuickFix) (Just (LSP.List [diag])) Nothing (Just cmd)
|
||||
title = "Apply hint:" <> head (T.lines m)
|
||||
-- need 'file', 'start_pos' and hint title (to distinguish between alternative suggestions at the same location)
|
||||
args = [toJSON (AOP (docId ^. LSP.uri) start code)]
|
||||
|
@ -148,11 +148,12 @@ runScheduler
|
||||
-- ^ A handler to run the requests' callback in your monad of choosing.
|
||||
-> Core.LspFuncs Config
|
||||
-- ^ The LspFuncs provided by haskell-lsp.
|
||||
-> PublishDiagnostics
|
||||
-> Maybe Bios.Cradle
|
||||
-- ^ Context in which the ghc thread is executed.
|
||||
-- Neccessary to obtain the libdir, for example.
|
||||
-> IO ()
|
||||
runScheduler Scheduler {..} errorHandler callbackHandler lf mcradle = do
|
||||
runScheduler Scheduler {..} errorHandler callbackHandler lf pubDiags mcradle = do
|
||||
let dEnv = DispatcherEnv
|
||||
{ cancelReqsTVar = requestsToCancel
|
||||
, wipReqsTVar = requestsInProgress
|
||||
@ -171,7 +172,7 @@ runScheduler Scheduler {..} errorHandler callbackHandler lf mcradle = do
|
||||
Just crdl -> Bios.getProjectGhcLibDir crdl
|
||||
|
||||
let runGhcDisp = runIdeGhcM mlibdir plugins lf stateVar $
|
||||
ghcDispatcher dEnv errorHandler callbackHandler ghcChanOut
|
||||
ghcDispatcher dEnv errorHandler pubDiags callbackHandler ghcChanOut
|
||||
runIdeDisp = runIdeM plugins lf stateVar $
|
||||
ideDispatcher dEnv errorHandler callbackHandler ideChanOut
|
||||
|
||||
@ -325,10 +326,11 @@ ghcDispatcher
|
||||
:: forall void m
|
||||
. DispatcherEnv
|
||||
-> ErrorHandler
|
||||
-> PublishDiagnostics
|
||||
-> CallbackHandler m
|
||||
-> Channel.OutChan (GhcRequest m)
|
||||
-> IdeGhcM void
|
||||
ghcDispatcher env@DispatcherEnv { docVersionTVar } errorHandler callbackHandler pin
|
||||
ghcDispatcher env@DispatcherEnv { docVersionTVar } errorHandler publishDiagnostics callbackHandler pin
|
||||
= do
|
||||
iniDynFlags <- getSessionDynFlags
|
||||
forever $ do
|
||||
@ -342,13 +344,13 @@ ghcDispatcher env@DispatcherEnv { docVersionTVar } errorHandler callbackHandler
|
||||
runner :: a -> IdeGhcM a -> IdeGhcM (IdeResult a)
|
||||
|
||||
runner a act = case context of
|
||||
Nothing -> runActionWithContext iniDynFlags Nothing a act
|
||||
Nothing -> runActionWithContext publishDiagnostics iniDynFlags Nothing a act
|
||||
Just uri -> case uriToFilePath uri of
|
||||
Just fp -> runActionWithContext iniDynFlags (Just fp) a act
|
||||
Just fp -> runActionWithContext publishDiagnostics iniDynFlags (Just fp) a act
|
||||
Nothing -> do
|
||||
debugm
|
||||
"ghcDispatcher:Got malformed uri, running action with default context"
|
||||
runActionWithContext iniDynFlags Nothing a act
|
||||
runActionWithContext publishDiagnostics iniDynFlags Nothing a act
|
||||
|
||||
let
|
||||
runWithCallback = do
|
||||
|
@ -186,7 +186,12 @@ run scheduler _origDir plugins captureFp = flip E.catches handlers $ do
|
||||
|
||||
Left (e :: Yaml.ParseException) -> do
|
||||
logm $ "Failed to parse `hie.yaml`: " ++ show e
|
||||
sf $ NotShowMessage $ fmServerShowMessageNotification J.MtError ("Couldn't parse hie.yaml: \n" <> T.pack (show e))
|
||||
sf $ NotShowMessage
|
||||
$ fmServerShowMessageNotification
|
||||
J.MtError
|
||||
( "Couldn't parse hie.yaml: \n"
|
||||
<> T.pack (Yaml.prettyPrintParseException e)
|
||||
)
|
||||
|
||||
let mcradle = case cradleRes of
|
||||
Left _ -> Nothing
|
||||
@ -196,23 +201,27 @@ run scheduler _origDir plugins captureFp = flip E.catches handlers $ do
|
||||
-- We launch the dispatcher after that so that the default cradle is
|
||||
-- recognized properly by ghc-mod
|
||||
flip labelThread "scheduler" =<<
|
||||
(forkIO (
|
||||
Scheduler.runScheduler scheduler errorHandler callbackHandler lf mcradle
|
||||
`E.catch` \(e :: E.SomeException) ->
|
||||
(errorm $ "Scheduler thread exited unexpectedly: " ++ show e)
|
||||
))
|
||||
forkIO
|
||||
( Scheduler.runScheduler scheduler errorHandler callbackHandler lf (publishDiagnostics' lf) mcradle
|
||||
`E.catch`
|
||||
\(e :: E.SomeException) ->
|
||||
errorm $ "Scheduler thread exited unexpectedly: " ++ show e
|
||||
)
|
||||
flip labelThread "reactor" =<<
|
||||
(forkIO (
|
||||
reactorFunc
|
||||
`E.catch` \(e :: E.SomeException) ->
|
||||
(errorm $ "Reactor thread exited unexpectedly: " ++ show e)
|
||||
))
|
||||
forkIO
|
||||
( reactorFunc
|
||||
`E.catch`
|
||||
\(e :: E.SomeException) ->
|
||||
errorm $ "Reactor thread exited unexpectedly: " ++ show e
|
||||
)
|
||||
|
||||
flip labelThread "diagnostics" =<<
|
||||
(forkIO (
|
||||
diagnosticsQueue tr
|
||||
`E.catch` \(e :: E.SomeException) ->
|
||||
(errorm $ "Diagnostic thread exited unexpectedly: " ++ show e)
|
||||
))
|
||||
forkIO
|
||||
( diagnosticsQueue tr
|
||||
`E.catch`
|
||||
\(e :: E.SomeException) ->
|
||||
errorm $ "Diagnostic thread exited unexpectedly: " ++ show e
|
||||
)
|
||||
|
||||
return Nothing
|
||||
|
||||
@ -354,10 +363,18 @@ updatePositionMap uri changes = pluginGetFile "updatePositionMap: " uri $ \file
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
publishDiagnostics :: (MonadIO m, MonadReader REnv m)
|
||||
=> Int -> J.NormalizedUri -> J.TextDocumentVersion -> DiagnosticsBySource -> m ()
|
||||
publishDiagnostics maxToSend uri' mv diags = do
|
||||
=> J.NormalizedUri -> J.TextDocumentVersion -> DiagnosticsBySource -> m ()
|
||||
publishDiagnostics uri' mv diags = do
|
||||
lf <- asks lspFuncs
|
||||
liftIO $ Core.publishDiagnosticsFunc lf maxToSend uri' mv diags
|
||||
publishDiagnostics' lf uri' mv diags
|
||||
|
||||
|
||||
publishDiagnostics' :: MonadIO m
|
||||
=> Core.LspFuncs Config -> J.NormalizedUri -> J.TextDocumentVersion -> DiagnosticsBySource -> m ()
|
||||
publishDiagnostics' lf uri' mv diags = do
|
||||
config <- liftIO $ fromMaybe Data.Default.def <$> Core.config lf
|
||||
liftIO $ Core.publishDiagnosticsFunc lf (maxNumberOfProblems config) uri' mv diags
|
||||
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
|
||||
@ -931,18 +948,17 @@ requestDiagnosticsNormal tn file mVer = do
|
||||
sendOneGhc :: J.DiagnosticSource -> (J.NormalizedUri, [Diagnostic]) -> R ()
|
||||
sendOneGhc pid (fileUri,ds) = do
|
||||
if any (hasSeverity J.DsError) ds
|
||||
then publishDiagnostics maxToSend fileUri Nothing
|
||||
then publishDiagnostics fileUri Nothing
|
||||
(Map.fromList [(Just "hlint",SL.toSortedList []),(Just pid,SL.toSortedList ds)])
|
||||
else sendOne pid (fileUri,ds)
|
||||
|
||||
sendOne pid (fileUri,ds) = do
|
||||
publishDiagnostics maxToSend fileUri Nothing (Map.fromList [(Just pid,SL.toSortedList ds)])
|
||||
publishDiagnostics fileUri Nothing (Map.fromList [(Just pid,SL.toSortedList ds)])
|
||||
|
||||
hasSeverity :: J.DiagnosticSeverity -> J.Diagnostic -> Bool
|
||||
hasSeverity sev (J.Diagnostic _ (Just s) _ _ _ _) = s == sev
|
||||
hasSeverity _ _ = False
|
||||
sendEmpty = publishDiagnostics maxToSend (J.toNormalizedUri file) Nothing (Map.fromList [(Just "bios",SL.toSortedList [])])
|
||||
maxToSend = maxNumberOfProblems clientConfig
|
||||
sendEmpty = publishDiagnostics (J.toNormalizedUri file) Nothing (Map.fromList [(Just "bios",SL.toSortedList [])])
|
||||
|
||||
let sendHlint = hlintOn clientConfig
|
||||
when sendHlint $ do
|
||||
|
@ -1,4 +1,4 @@
|
||||
resolver: lts-14.16
|
||||
resolver: lts-14.20
|
||||
packages:
|
||||
- .
|
||||
- hie-plugin-api
|
||||
|
@ -81,6 +81,7 @@ startServer = do
|
||||
(\lid errCode e -> logToChan logChan ("received an error", Left (lid, errCode, e)))
|
||||
(\g x -> g x)
|
||||
dummyLspFuncs
|
||||
(\_ _ _ -> return ())
|
||||
(Just crdl)
|
||||
|
||||
return (scheduler, logChan, dispatcher)
|
||||
|
@ -40,25 +40,18 @@ spec = describe "diagnostics providers" $ do
|
||||
reduceDiag ^. LSP.source `shouldBe` Just "hlint"
|
||||
|
||||
diags2a <- waitForDiagnostics
|
||||
-- liftIO $ show diags2a `shouldBe` ""
|
||||
|
||||
liftIO $ length diags2a `shouldBe` 2
|
||||
|
||||
-- docItem <- getDocItem file languageId
|
||||
sendNotification TextDocumentDidSave (DidSaveTextDocumentParams doc)
|
||||
-- diags2hlint <- waitForDiagnostics
|
||||
-- -- liftIO $ show diags2hlint `shouldBe` ""
|
||||
-- liftIO $ length diags2hlint `shouldBe` 3
|
||||
-- diags2liquid <- waitForDiagnostics
|
||||
-- liftIO $ length diags2liquid `shouldBe` 3
|
||||
-- -- liftIO $ show diags2 `shouldBe` ""
|
||||
diags3@(d:_) <- waitForDiagnostics
|
||||
-- liftIO $ show diags3 `shouldBe` ""
|
||||
|
||||
diags3@(d:_) <- waitForDiagnosticsSource "eg2"
|
||||
|
||||
liftIO $ do
|
||||
length diags3 `shouldBe` 3
|
||||
length diags3 `shouldBe` 1
|
||||
d ^. LSP.range `shouldBe` Range (Position 0 0) (Position 1 0)
|
||||
d ^. LSP.severity `shouldBe` Nothing
|
||||
d ^. LSP.code `shouldBe` Nothing
|
||||
d ^. LSP.source `shouldBe` Just "eg2"
|
||||
d ^. LSP.message `shouldBe` T.pack "Example plugin diagnostic, triggered byDiagnosticOnSave"
|
||||
|
||||
describe "typed hole errors" $
|
||||
|
@ -51,6 +51,7 @@ newPluginSpec = do
|
||||
(\_ _ _ -> return ())
|
||||
(\f x -> f x)
|
||||
dummyLspFuncs
|
||||
(\_ _ _ -> return ())
|
||||
(Just crdl)
|
||||
|
||||
updateDocument scheduler (filePathToUri "test") 3
|
||||
|
2
test/testdata/cabal-helper/implicit-exe/Setup.hs
vendored
Normal file
2
test/testdata/cabal-helper/implicit-exe/Setup.hs
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import Distribution.Simple
|
||||
main = defaultMain
|
1
test/testdata/cabal-helper/implicit-exe/cabal.project
vendored
Normal file
1
test/testdata/cabal-helper/implicit-exe/cabal.project
vendored
Normal file
@ -0,0 +1 @@
|
||||
packages: ./
|
17
test/testdata/cabal-helper/implicit-exe/implicit-exe.cabal
vendored
Normal file
17
test/testdata/cabal-helper/implicit-exe/implicit-exe.cabal
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
cabal-version: >=1.10
|
||||
name: implicit-exe
|
||||
version: 0.1.0.0
|
||||
license-file: LICENSE
|
||||
build-type: Simple
|
||||
|
||||
library
|
||||
exposed-modules: Lib
|
||||
hs-source-dirs: src
|
||||
build-depends: base >=4.8 && <4.14
|
||||
default-language: Haskell2010
|
||||
|
||||
|
||||
executable implicit-exe
|
||||
main-is: src/Exe.hs
|
||||
build-depends: base >=4.8 && <4.14, implicit-exe
|
||||
default-language: Haskell2010
|
4
test/testdata/cabal-helper/implicit-exe/src/Exe.hs
vendored
Normal file
4
test/testdata/cabal-helper/implicit-exe/src/Exe.hs
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
import Lib (someFunc)
|
||||
|
||||
main = someFunc
|
4
test/testdata/cabal-helper/implicit-exe/src/Lib.hs
vendored
Normal file
4
test/testdata/cabal-helper/implicit-exe/src/Lib.hs
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
module Lib (someFunc) where
|
||||
|
||||
someFunc :: IO ()
|
||||
someFunc = putStrLn "someFunc"
|
15
test/testdata/cabal-helper/mono-repo/A/A.cabal
vendored
Normal file
15
test/testdata/cabal-helper/mono-repo/A/A.cabal
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
cabal-version: >=2.0
|
||||
name: A
|
||||
version: 0.1.0.0
|
||||
build-type: Simple
|
||||
|
||||
library
|
||||
exposed-modules: MyLib
|
||||
build-depends: base >=4.9 && < 5
|
||||
default-language: Haskell2010
|
||||
|
||||
executable A
|
||||
main-is: Main.hs
|
||||
other-modules: MyLib
|
||||
build-depends: base >= 4.9 && < 5, A
|
||||
default-language: Haskell2010
|
8
test/testdata/cabal-helper/mono-repo/A/Main.hs
vendored
Normal file
8
test/testdata/cabal-helper/mono-repo/A/Main.hs
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
module Main where
|
||||
|
||||
import qualified MyLib (someFunc)
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
putStrLn "Hello, Haskell!"
|
||||
MyLib.someFunc
|
4
test/testdata/cabal-helper/mono-repo/A/MyLib.hs
vendored
Normal file
4
test/testdata/cabal-helper/mono-repo/A/MyLib.hs
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
module MyLib (someFunc) where
|
||||
|
||||
someFunc :: IO ()
|
||||
someFunc = putStrLn "someFunc"
|
2
test/testdata/cabal-helper/mono-repo/A/Setup.hs
vendored
Normal file
2
test/testdata/cabal-helper/mono-repo/A/Setup.hs
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import Distribution.Simple
|
||||
main = defaultMain
|
15
test/testdata/cabal-helper/mono-repo/B/B.cabal
vendored
Normal file
15
test/testdata/cabal-helper/mono-repo/B/B.cabal
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
cabal-version: >=2.0
|
||||
name: B
|
||||
version: 0.1.0.0
|
||||
build-type: Simple
|
||||
|
||||
library
|
||||
exposed-modules: MyLib
|
||||
build-depends: base >= 4.9 && < 5
|
||||
default-language: Haskell2010
|
||||
|
||||
executable B
|
||||
main-is: Main.hs
|
||||
other-modules: MyLib
|
||||
build-depends: base >= 4.9 && < 5, B
|
||||
default-language: Haskell2010
|
8
test/testdata/cabal-helper/mono-repo/B/Main.hs
vendored
Normal file
8
test/testdata/cabal-helper/mono-repo/B/Main.hs
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
module Main where
|
||||
|
||||
import qualified MyLib (someFunc)
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
putStrLn "Hello, Haskell!"
|
||||
MyLib.someFunc
|
4
test/testdata/cabal-helper/mono-repo/B/MyLib.hs
vendored
Normal file
4
test/testdata/cabal-helper/mono-repo/B/MyLib.hs
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
module MyLib (someFunc) where
|
||||
|
||||
someFunc :: IO ()
|
||||
someFunc = putStrLn "someFunc"
|
2
test/testdata/cabal-helper/mono-repo/B/Setup.hs
vendored
Normal file
2
test/testdata/cabal-helper/mono-repo/B/Setup.hs
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import Distribution.Simple
|
||||
main = defaultMain
|
9
test/testdata/cabal-helper/mono-repo/C/C.cabal
vendored
Normal file
9
test/testdata/cabal-helper/mono-repo/C/C.cabal
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
cabal-version: >=2.0
|
||||
name: C
|
||||
version: 0.1.0.0
|
||||
build-type: Simple
|
||||
|
||||
library
|
||||
exposed-modules: MyLib
|
||||
build-depends: base>= 4.9 && < 5
|
||||
default-language: Haskell2010
|
4
test/testdata/cabal-helper/mono-repo/C/MyLib.hs
vendored
Normal file
4
test/testdata/cabal-helper/mono-repo/C/MyLib.hs
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
module MyLib (someFunc) where
|
||||
|
||||
someFunc :: IO ()
|
||||
someFunc = putStrLn "someFunc"
|
2
test/testdata/cabal-helper/mono-repo/C/Setup.hs
vendored
Normal file
2
test/testdata/cabal-helper/mono-repo/C/Setup.hs
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import Distribution.Simple
|
||||
main = defaultMain
|
4
test/testdata/cabal-helper/mono-repo/cabal.project
vendored
Normal file
4
test/testdata/cabal-helper/mono-repo/cabal.project
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
packages:
|
||||
./A/
|
||||
./B/
|
||||
./C/
|
4
test/testdata/cabal-helper/simple-cabal/MyLib.hs
vendored
Normal file
4
test/testdata/cabal-helper/simple-cabal/MyLib.hs
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
module MyLib (someFunc) where
|
||||
|
||||
someFunc :: IO ()
|
||||
someFunc = putStrLn "someFunc"
|
2
test/testdata/cabal-helper/simple-cabal/Setup.hs
vendored
Normal file
2
test/testdata/cabal-helper/simple-cabal/Setup.hs
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import Distribution.Simple
|
||||
main = defaultMain
|
10
test/testdata/cabal-helper/simple-cabal/simple-cabal-test.cabal
vendored
Normal file
10
test/testdata/cabal-helper/simple-cabal/simple-cabal-test.cabal
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
cabal-version: >=1.10
|
||||
name: simple-cabal-test
|
||||
version: 0.1.0.0
|
||||
license-file: LICENSE
|
||||
build-type: Simple
|
||||
|
||||
library
|
||||
exposed-modules: MyLib
|
||||
build-depends: base >=4.12 && <4.13
|
||||
default-language: Haskell2010
|
4
test/testdata/cabal-helper/simple-stack/MyLib.hs
vendored
Normal file
4
test/testdata/cabal-helper/simple-stack/MyLib.hs
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
module MyLib (someFunc) where
|
||||
|
||||
someFunc :: IO ()
|
||||
someFunc = putStrLn "someFunc"
|
2
test/testdata/cabal-helper/simple-stack/Setup.hs
vendored
Normal file
2
test/testdata/cabal-helper/simple-stack/Setup.hs
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import Distribution.Simple
|
||||
main = defaultMain
|
10
test/testdata/cabal-helper/simple-stack/simple-stack-test.cabal
vendored
Normal file
10
test/testdata/cabal-helper/simple-stack/simple-stack-test.cabal
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
cabal-version: >=1.10
|
||||
name: simple-stack-test
|
||||
version: 0.1.0.0
|
||||
license-file: LICENSE
|
||||
build-type: Simple
|
||||
|
||||
library
|
||||
exposed-modules: MyLib
|
||||
build-depends: base >=4.12 && <4.13
|
||||
default-language: Haskell2010
|
2
test/testdata/cabal-helper/sub-package/Setup.hs
vendored
Normal file
2
test/testdata/cabal-helper/sub-package/Setup.hs
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import Distribution.Simple
|
||||
main = defaultMain
|
8
test/testdata/cabal-helper/sub-package/app/Main.hs
vendored
Normal file
8
test/testdata/cabal-helper/sub-package/app/Main.hs
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
module Main where
|
||||
|
||||
import qualified MyLib (someFunc)
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
putStrLn "Hello, Haskell!"
|
||||
MyLib.someFunc
|
4
test/testdata/cabal-helper/sub-package/plugins-api/PluginLib.hs
vendored
Normal file
4
test/testdata/cabal-helper/sub-package/plugins-api/PluginLib.hs
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
module PluginLib (someFunc) where
|
||||
|
||||
someFunc :: IO ()
|
||||
someFunc = putStrLn "someFunc"
|
2
test/testdata/cabal-helper/sub-package/plugins-api/Setup.hs
vendored
Normal file
2
test/testdata/cabal-helper/sub-package/plugins-api/Setup.hs
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import Distribution.Simple
|
||||
main = defaultMain
|
10
test/testdata/cabal-helper/sub-package/plugins-api/plugins-api.cabal
vendored
Normal file
10
test/testdata/cabal-helper/sub-package/plugins-api/plugins-api.cabal
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
cabal-version: >=1.10
|
||||
name: plugins-api
|
||||
version: 0.1.0.0
|
||||
license-file: LICENSE
|
||||
build-type: Simple
|
||||
|
||||
library
|
||||
exposed-modules: PluginLib
|
||||
build-depends: base >=4.12 && <4.13
|
||||
default-language: Haskell2010
|
6
test/testdata/cabal-helper/sub-package/src/MyLib.hs
vendored
Normal file
6
test/testdata/cabal-helper/sub-package/src/MyLib.hs
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
module MyLib (someFunc) where
|
||||
|
||||
import qualified PluginLib as L
|
||||
|
||||
someFunc :: IO ()
|
||||
someFunc = L.someFunc
|
17
test/testdata/cabal-helper/sub-package/sub-package.cabal
vendored
Normal file
17
test/testdata/cabal-helper/sub-package/sub-package.cabal
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
cabal-version: >=1.10
|
||||
name: sub-package
|
||||
version: 0.1.0.0
|
||||
license-file: LICENSE
|
||||
build-type: Simple
|
||||
|
||||
library
|
||||
exposed-modules: MyLib
|
||||
build-depends: base >=4.12 && <4.13, plugins-api
|
||||
hs-source-dirs: src
|
||||
default-language: Haskell2010
|
||||
|
||||
executable sub-package
|
||||
main-is: Main.hs
|
||||
build-depends: base >=4.12 && <4.13, sub-package
|
||||
hs-source-dirs: app
|
||||
default-language: Haskell2010
|
187
test/unit/CabalHelperSpec.hs
Normal file
187
test/unit/CabalHelperSpec.hs
Normal file
@ -0,0 +1,187 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
module CabalHelperSpec where
|
||||
|
||||
import Haskell.Ide.Engine.Cradle
|
||||
import Test.Hspec
|
||||
import System.FilePath
|
||||
import System.Directory (getCurrentDirectory, removeFile)
|
||||
import TestUtils
|
||||
|
||||
rootPath :: FilePath -> FilePath
|
||||
rootPath cwd = cwd </> "test" </> "testdata" </> "cabal-helper"
|
||||
|
||||
implicitExePath :: FilePath -> FilePath
|
||||
implicitExePath cwd = rootPath cwd </> "implicit-exe"
|
||||
|
||||
monoRepoPath :: FilePath -> FilePath
|
||||
monoRepoPath cwd = rootPath cwd </> "mono-repo"
|
||||
|
||||
subPackagePath :: FilePath -> FilePath
|
||||
subPackagePath cwd = rootPath cwd </> "sub-package"
|
||||
|
||||
simpleCabalPath :: FilePath -> FilePath
|
||||
simpleCabalPath cwd = rootPath cwd </> "simple-cabal"
|
||||
|
||||
simpleStackPath :: FilePath -> FilePath
|
||||
simpleStackPath cwd = rootPath cwd </> "simple-stack"
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ setupStackFiles $ do
|
||||
describe "cabal-helper spec" $ do
|
||||
describe "find cabal entry point spec" findCabalHelperEntryPointSpec
|
||||
describe "cradle discovery" cabalHelperCradleSpec
|
||||
|
||||
cabalHelperCradleSpec :: Spec
|
||||
cabalHelperCradleSpec = do
|
||||
cwd <- runIO getCurrentDirectory
|
||||
describe "dummy filepath, finds none-cradle" $ do
|
||||
it "implicit exe, dummy filepath" $ do
|
||||
crdl <- cabalHelperCradle (implicitExePath cwd </> "File.hs")
|
||||
crdl `shouldSatisfy` isCabalCradle
|
||||
it "mono repo, dummy filepath" $ do
|
||||
crdl <- cabalHelperCradle (monoRepoPath cwd </> "File.hs")
|
||||
crdl `shouldSatisfy` isCabalCradle
|
||||
it "stack repo, dummy filepath" $ do
|
||||
crdl <- cabalHelperCradle (simpleStackPath cwd </> "File.hs")
|
||||
crdl `shouldSatisfy` isStackCradle
|
||||
it "cabal repo, dummy filepath" $
|
||||
pendingWith "Can not work because of global `cabal.project`"
|
||||
-- crdl <- cabalHelperCradle (simpleCabalPath cwd </> "File.hs")
|
||||
-- crdl `shouldSatisfy` isCabalCradle
|
||||
it "sub package, dummy filepath" $ do
|
||||
crdl <- cabalHelperCradle (subPackagePath cwd </> "File.hs")
|
||||
crdl `shouldSatisfy` isStackCradle
|
||||
|
||||
describe "Existing projects" $ do
|
||||
it "implicit exe" $ do
|
||||
crdl <- cabalHelperCradle (implicitExePath cwd </> "src" </> "Exe.hs")
|
||||
crdl `shouldSatisfy` isCabalCradle
|
||||
it "mono repo" $ do
|
||||
crdl <- cabalHelperCradle (monoRepoPath cwd </> "A" </> "Main.hs")
|
||||
crdl `shouldSatisfy` isCabalCradle
|
||||
it "stack repo" $ do
|
||||
crdl <- cabalHelperCradle (simpleStackPath cwd </> "MyLib.hs")
|
||||
crdl `shouldSatisfy` isStackCradle
|
||||
it "cabal repo" $
|
||||
pendingWith "Can not work because of global `cabal.project`"
|
||||
-- crdl <- cabalHelperCradle (simpleCabalPath cwd </> "MyLib.hs")
|
||||
-- crdl `shouldSatisfy` isCabalCradle
|
||||
it "sub package" $ do
|
||||
crdl <- cabalHelperCradle (subPackagePath cwd </> "plugins-api" </> "PluginLib.hs")
|
||||
crdl `shouldSatisfy` isStackCradle
|
||||
|
||||
findCabalHelperEntryPointSpec :: Spec
|
||||
findCabalHelperEntryPointSpec = do
|
||||
cwd <- runIO getCurrentDirectory
|
||||
describe "implicit exe" $ do
|
||||
it "Find project root with dummy filepath" $ do
|
||||
let dummyFile = implicitExePath cwd </> "File.hs"
|
||||
cabalTest dummyFile
|
||||
it "Find project root from source component" $ do
|
||||
let libFile = implicitExePath cwd </> "src" </> "Lib.hs"
|
||||
cabalTest libFile
|
||||
it "Find project root from executable component" $ do
|
||||
let mainFile = implicitExePath cwd </> "src" </> "Exe.hs"
|
||||
cabalTest mainFile
|
||||
|
||||
describe "mono repo" $ do
|
||||
it "Find project root with dummy filepath" $ do
|
||||
let dummyFile = monoRepoPath cwd </> "File.hs"
|
||||
cabalTest dummyFile
|
||||
it "Find project root with existing executable" $ do
|
||||
let mainFile = monoRepoPath cwd </> "A" </> "Main.hs"
|
||||
cabalTest mainFile
|
||||
|
||||
describe "sub package repo" $ do
|
||||
it "Find project root with dummy filepath" $ do
|
||||
let dummyFile = subPackagePath cwd </> "File.hs"
|
||||
stackTest dummyFile
|
||||
it "Find project root with existing executable" $ do
|
||||
let mainFile = subPackagePath cwd </> "plugins-api" </> "PluginLib.hs"
|
||||
stackTest mainFile
|
||||
|
||||
describe "stack repo" $ do
|
||||
it "Find project root with dummy filepath" $ do
|
||||
let dummyFile = simpleStackPath cwd </> "File.hs"
|
||||
stackTest dummyFile
|
||||
it "Find project root with real filepath" $ do
|
||||
let dummyFile = simpleStackPath cwd </> "MyLib.hs"
|
||||
stackTest dummyFile
|
||||
|
||||
describe "simple cabal repo" $
|
||||
it "Find porject root with dummy filepath" $
|
||||
pendingWith "Change test-setup, we will always find `cabal.project` in root dir"
|
||||
|
||||
-- -------------------------------------------------------------
|
||||
|
||||
cabalTest :: FilePath -> IO ()
|
||||
cabalTest fp = do
|
||||
entryPoint <- findCabalHelperEntryPoint fp
|
||||
let Just proj = entryPoint
|
||||
isCabal = isCabalProject proj
|
||||
shouldBe isCabal True
|
||||
|
||||
stackTest :: FilePath -> IO ()
|
||||
stackTest fp = do
|
||||
entryPoint <- findCabalHelperEntryPoint fp
|
||||
let Just proj = entryPoint
|
||||
isStack = isStackProject proj
|
||||
shouldBe isStack True
|
||||
|
||||
-- -------------------------------------------------------------
|
||||
|
||||
setupStackFiles :: IO ()
|
||||
setupStackFiles = do
|
||||
resolver <- readResolver
|
||||
cwd <- getCurrentDirectory
|
||||
writeFile (implicitExePath cwd </> "stack.yaml") (standardStackYaml resolver)
|
||||
writeFile (monoRepoPath cwd </> "stack.yaml") (monoRepoStackYaml resolver)
|
||||
writeFile (subPackagePath cwd </> "stack.yaml") (subPackageStackYaml resolver)
|
||||
writeFile (simpleStackPath cwd </> "stack.yaml") (standardStackYaml resolver)
|
||||
|
||||
|
||||
cleanupStackFiles :: IO ()
|
||||
cleanupStackFiles = do
|
||||
cwd <- getCurrentDirectory
|
||||
removeFile (implicitExePath cwd </> "stack.yaml")
|
||||
removeFile (monoRepoPath cwd </> "stack.yaml")
|
||||
removeFile (subPackagePath cwd </> "stack.yaml")
|
||||
removeFile (simpleStackPath cwd </> "stack.yaml")
|
||||
|
||||
-- -------------------------------------------------------------
|
||||
|
||||
standardStackYaml :: String -> String
|
||||
standardStackYaml resolver = unlines
|
||||
[ "# WARNING: THIS FILE IS AUTOGENERATED IN test/utils/CabalHelperSpec. IT WILL BE OVERWRITTEN ON EVERY TEST RUN"
|
||||
, "resolver: " ++ resolver
|
||||
, "packages:"
|
||||
, "- '.'"
|
||||
, "extra-deps: []"
|
||||
, "flags: {}"
|
||||
, "extra-package-dbs: []"
|
||||
]
|
||||
|
||||
monoRepoStackYaml :: String -> String
|
||||
monoRepoStackYaml resolver = unlines
|
||||
[ "# WARNING: THIS FILE IS AUTOGENERATED IN test/utils/CabalHelperSpec. IT WILL BE OVERWRITTEN ON EVERY TEST RUN"
|
||||
, "resolver: " ++ resolver
|
||||
, "packages:"
|
||||
, "- 'A'"
|
||||
, "- 'B'"
|
||||
, "- 'C'"
|
||||
, "extra-deps: []"
|
||||
, "flags: {}"
|
||||
, "extra-package-dbs: []"
|
||||
]
|
||||
|
||||
subPackageStackYaml :: String -> String
|
||||
subPackageStackYaml resolver = unlines
|
||||
[ "# WARNING: THIS FILE IS AUTOGENERATED IN test/unit/CabalHelperSpec. IT WILL BE OVERWRITTEN ON EVERY TEST RUN"
|
||||
, "resolver: " ++ resolver
|
||||
, "packages:"
|
||||
, "- '.'"
|
||||
, "- 'plugins-api'"
|
||||
, "extra-deps: []"
|
||||
, "flags: {}"
|
||||
, "extra-package-dbs: []"
|
||||
]
|
@ -10,6 +10,7 @@ module TestUtils
|
||||
, runIGM
|
||||
, ghcVersion, GhcVersion(..)
|
||||
, logFilePath
|
||||
, readResolver
|
||||
, hieCommand
|
||||
, hieCommandVomit
|
||||
, hieCommandExamplePlugin
|
||||
@ -187,7 +188,7 @@ logFilePath = "hie-" ++ stackYaml ++ ".log"
|
||||
-- on PATH. Cabal seems to respond to @build-tool-depends@ specifically while
|
||||
-- stack just puts all project executables on PATH.
|
||||
hieCommand :: String
|
||||
hieCommand = "hie --bios-verbose -d -l test-logs/" ++ logFilePath
|
||||
hieCommand = "hie --lsp --bios-verbose -d -l test-logs/" ++ logFilePath
|
||||
|
||||
hieCommandVomit :: String
|
||||
hieCommandVomit = hieCommand ++ " --vomit"
|
||||
|
66
update-index-state.sh
Executable file
66
update-index-state.sh
Executable file
@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
|
||||
status_message() {
|
||||
printf "\\033[0;32m%s\\033[0m\\n" "$1"
|
||||
}
|
||||
|
||||
error_message() {
|
||||
printf "\\033[0;31m%s\\033[0m\\n" "$1"
|
||||
}
|
||||
|
||||
SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )"
|
||||
CACHE_LOCATION="${HOME}/.cabal/packages/hackage.haskell.org/01-index.cache"
|
||||
|
||||
if [ ! -f "${CACHE_LOCATION}" ] ; then
|
||||
error_message "${CACHE_LOCATION} does not exist, did you run 'cabal update'?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${SCRIPTPATH}/cabal.project" ] ; then
|
||||
error_message "Could not find ${SCRIPTPATH}/cabal.project, skipping index state update."
|
||||
exit 3
|
||||
fi
|
||||
|
||||
cabal v2-update
|
||||
|
||||
arch=$(getconf LONG_BIT)
|
||||
|
||||
case "${arch}" in
|
||||
32)
|
||||
byte_size=4
|
||||
magic_word="CABA1002"
|
||||
;;
|
||||
64)
|
||||
byte_size=8
|
||||
magic_word="00000000CABA1002"
|
||||
;;
|
||||
*)
|
||||
error_message "Unknown architecture (long bit): ${arch}"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
# This is the logic to parse the binary format of 01-index.cache.
|
||||
# The first word is a magic 'caba1002', the second one is the timestamp in unix epoch.
|
||||
# Better than copying the cabal-install source code.
|
||||
if [ "$(xxd -u -p -l${byte_size} -s 0 "${CACHE_LOCATION}")" != "${magic_word}" ] ; then
|
||||
error_message "Magic word does not match!"
|
||||
exit 4
|
||||
fi
|
||||
cache_timestamp=$(echo "ibase=16;obase=A;$(xxd -u -p -l${byte_size} -s ${byte_size} "${CACHE_LOCATION}")" | bc)
|
||||
|
||||
# If we got junk from the binary file, this should fail.
|
||||
cache_date=$(date --utc --date "@${cache_timestamp}" "+%FT%TZ")
|
||||
|
||||
|
||||
status_message "Updating index state in ${SCRIPTPATH}/cabal.project"
|
||||
|
||||
if grep -q "^index-state: .*" "${SCRIPTPATH}/cabal.project" ; then
|
||||
awk '/index-state:/ {gsub(/.*/, "index-state: '${cache_date}'")}; { print }' "${SCRIPTPATH}/cabal.project" > "${SCRIPTPATH}/cabal.project.tmp"
|
||||
mv "${SCRIPTPATH}/cabal.project.tmp" "${SCRIPTPATH}/cabal.project"
|
||||
else
|
||||
printf "index-state: %s\n" "${cache_date}" >> "${SCRIPTPATH}/cabal.project"
|
||||
fi
|
||||
|
Loading…
Reference in New Issue
Block a user