From e74cc62bc24bc50f3d8aaf230ea059b17b3b6724 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Mon, 6 Nov 2017 17:22:31 +0100 Subject: [PATCH 01/36] haskell-cabal-sandboxing: use cabal sandboxes to avoid breaking the global db --- cmake/Modules/LibAddHaskellPlugin.cmake | 11 ++--------- src/bindings/haskell/CMakeLists.txt | 9 +++++++-- src/plugins/haskell/haskell.cabal.in | 3 ++- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/cmake/Modules/LibAddHaskellPlugin.cmake b/cmake/Modules/LibAddHaskellPlugin.cmake index 74ad7481cc7..d9effbdad9d 100644 --- a/cmake/Modules/LibAddHaskellPlugin.cmake +++ b/cmake/Modules/LibAddHaskellPlugin.cmake @@ -156,22 +156,15 @@ macro (add_haskell_plugin target) DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" ) - # register the bindings for the compilation - add_custom_command ( - OUTPUT "${CMAKE_BINARY_DIR}/src/bindings/haskell/${target}-register" - COMMAND ${CABAL_EXECUTABLE} register --inplace - COMMAND ${CMAKE_COMMAND} ARGS -E touch "${target}-register" - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src/bindings/haskell - DEPENDS c2hs_haskell - ) add_custom_command ( OUTPUT ${PLUGIN_HASKELL_NAME} # this way it will generate predictable output filenames # and compile the haskell part of this plugin with cabal + COMMAND ${CABAL_EXECUTABLE} sandbox init --sandbox "${CMAKE_CURRENT_BINARY_DIR}/../../bindings/haskell/.cabal-sandbox" COMMAND ${CABAL_EXECUTABLE} --ipid=${target} ${CABAL_OPTS} configure COMMAND ${CABAL_EXECUTABLE} build WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS "${CMAKE_BINARY_DIR}/src/bindings/haskell/${target}-register" + DEPENDS c2hs_haskell "${CMAKE_SOURCE_DIR}/src/plugins/haskell/Elektra/Haskell.hs" ) add_custom_target (${target} DEPENDS ${PLUGIN_HASKELL_NAME}) diff --git a/src/bindings/haskell/CMakeLists.txt b/src/bindings/haskell/CMakeLists.txt index 3760f57af91..01b12737357 100644 --- a/src/bindings/haskell/CMakeLists.txt +++ b/src/bindings/haskell/CMakeLists.txt @@ -44,8 +44,13 @@ if (NOT BUILD_STATIC) add_custom_command ( OUTPUT ${BINDING_HASKELL_NAME} + COMMAND ${CABAL_EXECUTABLE} sandbox init + COMMAND ${CABAL_EXECUTABLE} install hspec QuickCheck COMMAND ${CABAL_EXECUTABLE} --ipid=libelektra-haskell-${KDB_VERSION} ${CABAL_OPTS} configure + # this only installs to the sandbox so plugins can depend on it + # without breaking the global db through shared sandboxes COMMAND ${CABAL_EXECUTABLE} build + COMMAND ${CABAL_EXECUTABLE} install WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/libelektra-haskell.cabal" "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/Key.chs" @@ -64,8 +69,8 @@ if (NOT BUILD_STATIC) "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/" ) endif (BUILD_SHARED OR BUILD_FULL) - # build and install it to the cabal db - install (CODE "execute_process (COMMAND ${CABAL_EXECUTABLE} install WORKING_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}\")") + # build and install it to the global db + install (CODE "execute_process (COMMAND ${CABAL_EXECUTABLE} --ignore-sandbox install WORKING_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}\")") if (BUILD_TESTING) # recompile with tests enabled, to get the dependency graph for the static versions correct diff --git a/src/plugins/haskell/haskell.cabal.in b/src/plugins/haskell/haskell.cabal.in index 441bdbecce5..b02920902bc 100644 --- a/src/plugins/haskell/haskell.cabal.in +++ b/src/plugins/haskell/haskell.cabal.in @@ -14,7 +14,8 @@ cabal-version: >=1.10 library hs-source-dirs: "@CMAKE_CURRENT_SOURCE_DIR@" exposed-modules: Elektra.Haskell - build-depends: base >= 4.7 && < 5, libelektra-haskell + build-depends: base >= 4.7 && < 5 + , libelektra-haskell default-language: Haskell2010 other-extensions: ForeignFunctionInterface ghc-options: -fPIC From a2efb196a513d2186ca31655ce8f71ec6d774abb Mon Sep 17 00:00:00 2001 From: e1528532 Date: Sat, 11 Nov 2017 17:29:34 +0100 Subject: [PATCH 02/36] haskell-cabal-sandboxing: enable caching, reenable tests in travis --- .travis.yml | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/.travis.yml b/.travis.yml index c2c04722514..a2bf4b31261 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,13 @@ language: cpp +cache: + directories: + $HOME/.m2 + $HOME/.cabal + $HOME/Library/Haskell + $TRAVIS_BUILD_DIR/../build/src/bindings/haskell/.cabal-sandbox + $TRAVIS_BUILD_DIR/../build/src/plugins/haskell/.cabal-sandbox + # # Define the build matrix # @@ -30,13 +38,6 @@ matrix: compiler: gcc env: [ FULL=ON ] - # HASKELL: Only build Haskell binding and plugin - - - os: osx - osx_image: xcode9.1 - compiler: clang - env: [ HASKELL=ON ] - - os: osx osx_image: xcode9.1 compiler: gcc @@ -58,7 +59,7 @@ before_install: brew install ninja fi - | - if [[ "$TRAVIS_OS_NAME" == "osx" && "$FAST" != "ON" && "$HASKELL" != "ON" ]]; then + if [[ "$TRAVIS_OS_NAME" == "osx" && "$FAST" != "ON" ]]; then rvm install 2.3.1 rvm use 2.3.1 gem install test-unit --no-document @@ -75,13 +76,13 @@ before_install: brew install xerces-c fi brew install augeas + brew install cabal-install brew install checkbashisms brew install dbus brew install discount brew install gobject-introspection brew install libgcrypt brew install libgit2 - brew install libuv brew install lua brew install openssl brew install python @@ -91,14 +92,23 @@ before_install: brew install yajl pip2 install cheetah # Required by kdb-gen export Qt5_DIR=/usr/local/opt/qt5 - fi - - | - if [[ "$TRAVIS_OS_NAME" == "osx" && "$HASKELL" == "ON" ]] ; then + brew install cabal-install cabal update - cabal install happy alex + # avoid reinstalls if we already have them cached + which happy + if [[ "$?" == 1 ]] + cabal install happy + fi + which alex + if [[ "$?" == 1 ]] + cabal install alex + fi + which c2hs + if [[ "$?" == 1 ]] + cabal install c2hs + fi PATH=$PATH:"$HOME/.cabal/bin" - cabal install QuickCheck hspec c2hs fi - | if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then @@ -107,7 +117,6 @@ before_install: sudo apt-get install ninja-build sudo apt-get install libboost-all-dev sudo apt-get install libyaml-cpp-dev - sudo apt-get install libuv-dev fi # @@ -127,7 +136,6 @@ before_script: if [[ "$TRAVIS_OS_NAME" == "linux" && "$CC" == "clang" || "$TRAVIS_OS_NAME" == "osx" && "${CC:0:3}" == "gcc" ]]; then CMAKE_OPT+=("-DCOMMON_FLAGS=-Werror") fi - - if [[ $HASKELL == ON ]]; then bindings="haskell"; fi - | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then python2_ver=$(python2 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') && \ @@ -143,7 +151,7 @@ before_script: -GNinja -DBUILD_STATIC=OFF -DPLUGINS="${plugins:-ALL;-jni}" - -DBINDINGS="${bindings}" + -DBINDINGS="${bindings:-ALL;haskell}" -DENABLE_DEBUG=ON -DTOOLS=ALL -DINSTALL_SYSTEM_FILES=OFF @@ -162,9 +170,5 @@ script: ninja run_all else ninja install - # Unfortunately the Haskell tests do not work currently: - # https://github.com/ElektraInitiative/libelektra/issues/1631 - if [[ "$HASKELL" != "ON" ]]; then - ninja run_all && kdb run_all - fi + ninja run_all && kdb run_all fi From f757e3ab13e07395b880ad760850c33ad038209d Mon Sep 17 00:00:00 2001 From: e1528532 Date: Sat, 11 Nov 2017 17:46:08 +0100 Subject: [PATCH 03/36] haskell-cabal-sandboxing: fix travis file to not use $? --- .travis.yml | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index a2bf4b31261..d428e8c0d18 100644 --- a/.travis.yml +++ b/.travis.yml @@ -96,18 +96,9 @@ before_install: brew install cabal-install cabal update # avoid reinstalls if we already have them cached - which happy - if [[ "$?" == 1 ]] - cabal install happy - fi - which alex - if [[ "$?" == 1 ]] - cabal install alex - fi - which c2hs - if [[ "$?" == 1 ]] - cabal install c2hs - fi + which happy || cabal install happy + which alex || cabal install alex + which c2hs || cabal install c2hs PATH=$PATH:"$HOME/.cabal/bin" fi - | From a016f69027f56434788efb1860504e1a0d11518e Mon Sep 17 00:00:00 2001 From: e1528532 Date: Sat, 11 Nov 2017 17:52:00 +0100 Subject: [PATCH 04/36] haskell-cabal-sandboxing: fix build due to caching change --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d428e8c0d18..44f7625da78 100644 --- a/.travis.yml +++ b/.travis.yml @@ -119,7 +119,8 @@ before_script: - > [[ "$TRAVIS_OS_NAME" == "linux" ]] && INSTALL_DIR="$PWD/install" || INSTALL_DIR="/usr/local" - SYSTEM_DIR="$PWD/kdbsystem" - - mkdir build && cd build + # it might be partially cached due to the cabal sandbox caching + - cd build || mkdir build && cd build - > [[ $ASAN == ON ]] && CMAKE_OPT=(-DENABLE_ASAN=ON) || CMAKE_OPT=() - if [[ $FULL == ON ]]; then CMAKE_OPT+=(-DBUILD_FULL=ON); fi From e97ef12a64c46986b819631c29cbd1aa9c9c0db4 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Sat, 11 Nov 2017 18:02:15 +0100 Subject: [PATCH 05/36] haskell-cabal-sandboxing: fix travis build --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 44f7625da78..f0a1066f457 100644 --- a/.travis.yml +++ b/.travis.yml @@ -119,8 +119,8 @@ before_script: - > [[ "$TRAVIS_OS_NAME" == "linux" ]] && INSTALL_DIR="$PWD/install" || INSTALL_DIR="/usr/local" - SYSTEM_DIR="$PWD/kdbsystem" - # it might be partially cached due to the cabal sandbox caching - - cd build || mkdir build && cd build + # gets created due to sandbox caching automatically + - cd build - > [[ $ASAN == ON ]] && CMAKE_OPT=(-DENABLE_ASAN=ON) || CMAKE_OPT=() - if [[ $FULL == ON ]]; then CMAKE_OPT+=(-DBUILD_FULL=ON); fi From 0aad3776f8d07e6c2c69bbdf1382fe113ae52e16 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Sat, 11 Nov 2017 20:06:22 +0100 Subject: [PATCH 06/36] haskell-cabal-sandboxing: manage hspec and QuickCheck via sandbox builds, automatically install any dependencies in the sandbox haskell plugin authors require --- cmake/Modules/FindHaskell.cmake | 14 ++++---------- cmake/Modules/LibAddHaskellPlugin.cmake | 2 ++ src/bindings/haskell/CMakeLists.txt | 3 ++- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/cmake/Modules/FindHaskell.cmake b/cmake/Modules/FindHaskell.cmake index 6b75d4926ae..ebf9e12dcae 100644 --- a/cmake/Modules/FindHaskell.cmake +++ b/cmake/Modules/FindHaskell.cmake @@ -53,16 +53,10 @@ if (GHC-PKG_EXECUTABLE) set (GHC_QUICKCHECK_FOUND 0) endif (GHC_QUICKCHECK_FOUND EQUAL 0) - if (GHC_HSPEC_FOUND OR NOT BUILD_TESTING) - if (GHC_QUICKCHECK_FOUND OR NOT BUILD_TESTING) - # all set, have fun with haskell! - set (HASKELL_FOUND 1) - else (GHC_QUICKCHECK_FOUND OR NOT BUILD_TESTING) - set (HASKELL_NOTFOUND_INFO "QuickCheck library not found") - endif (GHC_QUICKCHECK_FOUND OR NOT BUILD_TESTING) - else (GHC_HSPEC_FOUND OR NOT BUILD_TESTING) - set (HASKELL_NOTFOUND_INFO "hspec library not found") - endif (GHC_HSPEC_FOUND OR NOT BUILD_TESTING) + # By using cabal sandboxes we can install hspec and QuickCheck to the sandbox without + # any concerns as they are independent from the global environment. So they are not required. + # All set, have fun with haskell! + set (HASKELL_FOUND 1) else (GHC-PKG_EXECUTABLE) set (HASKELL_NOTFOUND_INFO "ghc-pkg not found") diff --git a/cmake/Modules/LibAddHaskellPlugin.cmake b/cmake/Modules/LibAddHaskellPlugin.cmake index d9effbdad9d..5fe745e29a6 100644 --- a/cmake/Modules/LibAddHaskellPlugin.cmake +++ b/cmake/Modules/LibAddHaskellPlugin.cmake @@ -161,6 +161,8 @@ macro (add_haskell_plugin target) # this way it will generate predictable output filenames # and compile the haskell part of this plugin with cabal COMMAND ${CABAL_EXECUTABLE} sandbox init --sandbox "${CMAKE_CURRENT_BINARY_DIR}/../../bindings/haskell/.cabal-sandbox" + # ensure any further dependencies added by plugin developers get installed to the sandbox + COMMAND ${CABAL_EXECUTABLE} install --only-dependencies COMMAND ${CABAL_EXECUTABLE} --ipid=${target} ${CABAL_OPTS} configure COMMAND ${CABAL_EXECUTABLE} build WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} diff --git a/src/bindings/haskell/CMakeLists.txt b/src/bindings/haskell/CMakeLists.txt index 01b12737357..8587d287352 100644 --- a/src/bindings/haskell/CMakeLists.txt +++ b/src/bindings/haskell/CMakeLists.txt @@ -45,7 +45,6 @@ if (NOT BUILD_STATIC) add_custom_command ( OUTPUT ${BINDING_HASKELL_NAME} COMMAND ${CABAL_EXECUTABLE} sandbox init - COMMAND ${CABAL_EXECUTABLE} install hspec QuickCheck COMMAND ${CABAL_EXECUTABLE} --ipid=libelektra-haskell-${KDB_VERSION} ${CABAL_OPTS} configure # this only installs to the sandbox so plugins can depend on it # without breaking the global db through shared sandboxes @@ -85,6 +84,8 @@ if (NOT BUILD_STATIC) ) add_custom_command ( OUTPUT ${HASKELL_TESTS} + # they are getting installed to the sandbox, and they get cached to avoid reinstalling every time + COMMAND ${CABAL_EXECUTABLE} install hspec QuickCheck COMMAND ${CABAL_EXECUTABLE} --ipid=libelektra-haskell-${KDB_VERSION} ${CABAL_OPTS} --enable-tests configure COMMAND ${CABAL_EXECUTABLE} build WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} From 2e3c8632cc7c3e8464da5cf3d0d8af94d54af20e Mon Sep 17 00:00:00 2001 From: e1528532 Date: Wed, 22 Nov 2017 19:46:38 +0100 Subject: [PATCH 07/36] haskell-cabal-sandboxing: adjust cabal options for faster dependency build times, add ctest parallelization --- .travis.yml | 8 +++++--- src/bindings/haskell/CMakeLists.txt | 5 ++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index f0a1066f457..9a86cea5b80 100644 --- a/.travis.yml +++ b/.travis.yml @@ -96,9 +96,10 @@ before_install: brew install cabal-install cabal update # avoid reinstalls if we already have them cached - which happy || cabal install happy - which alex || cabal install alex - which c2hs || cabal install c2hs + # disable everything that slows down compilation times of the dependencies to have reasonable travis build times + which happy || cabal install -disable-optimization --disable-documentation --disable-profiling --disable-tests -j happy + which alex || cabal install -disable-optimization --disable-documentation --disable-profiling --disable-tests -j alex + which c2hs || cabal install -disable-optimization --disable-documentation --disable-profiling --disable-tests -j c2hs PATH=$PATH:"$HOME/.cabal/bin" fi - | @@ -154,6 +155,7 @@ before_script: $TRAVIS_BUILD_DIR - export PATH=$PATH:"$INSTALL_DIR/bin" - export LD_LIBRARY_PATH="$INSTALL_DIR/lib" + - export CTEST_PARALLEL_LEVEL=4 script: - ninja diff --git a/src/bindings/haskell/CMakeLists.txt b/src/bindings/haskell/CMakeLists.txt index 8587d287352..e7bfc1d4691 100644 --- a/src/bindings/haskell/CMakeLists.txt +++ b/src/bindings/haskell/CMakeLists.txt @@ -46,6 +46,8 @@ if (NOT BUILD_STATIC) OUTPUT ${BINDING_HASKELL_NAME} COMMAND ${CABAL_EXECUTABLE} sandbox init COMMAND ${CABAL_EXECUTABLE} --ipid=libelektra-haskell-${KDB_VERSION} ${CABAL_OPTS} configure + # disable everything that slows down compilation times of the dependencies to have reasonable travis build times + COMMAND ${CABAL_EXECUTABLE} install --disable-optimization --disable-documentation --disable-profiling --disable-tests -j --only-dependencies # this only installs to the sandbox so plugins can depend on it # without breaking the global db through shared sandboxes COMMAND ${CABAL_EXECUTABLE} build @@ -85,7 +87,8 @@ if (NOT BUILD_STATIC) add_custom_command ( OUTPUT ${HASKELL_TESTS} # they are getting installed to the sandbox, and they get cached to avoid reinstalling every time - COMMAND ${CABAL_EXECUTABLE} install hspec QuickCheck + # disable everything that slows down compilation times of the dependencies to have reasonable travis build times + COMMAND ${CABAL_EXECUTABLE} install --disable-optimization --disable-documentation --disable-profiling -j --enable-tests --only-dependencies COMMAND ${CABAL_EXECUTABLE} --ipid=libelektra-haskell-${KDB_VERSION} ${CABAL_OPTS} --enable-tests configure COMMAND ${CABAL_EXECUTABLE} build WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} From bb5f2514224b6503e806e1d8210431952c3a96b2 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Wed, 22 Nov 2017 20:02:22 +0100 Subject: [PATCH 08/36] haskell-cabal-sandboxing: disable test parallelization again, seems to break asan checks --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9a86cea5b80..8a24ebdcb1a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -155,7 +155,6 @@ before_script: $TRAVIS_BUILD_DIR - export PATH=$PATH:"$INSTALL_DIR/bin" - export LD_LIBRARY_PATH="$INSTALL_DIR/lib" - - export CTEST_PARALLEL_LEVEL=4 script: - ninja From 5ced30d08ab568e3acb4438c4f4fe1420ba26821 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Wed, 29 Nov 2017 21:11:20 +0100 Subject: [PATCH 09/36] haskell-cabal-sandboxing: reverse travis changes, add keyGetRelativeName to the bindings --- .travis.yml | 27 ++++++++++++++----- cmake/Modules/LibAddHaskellPlugin.cmake | 3 ++- src/bindings/haskell/CMakeLists.txt | 8 +++--- .../haskell/libelektra-haskell.cabal.in | 3 ++- src/bindings/haskell/src/Elektra/Ease.chs | 9 +++++++ 5 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 src/bindings/haskell/src/Elektra/Ease.chs diff --git a/.travis.yml b/.travis.yml index 8a24ebdcb1a..9a7b17923e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,6 +46,13 @@ matrix: osx_image: xcode9.1 compiler: clang + # HASKELL: Only build Haskell binding and plugin + + - os: osx + osx_image: xcode9.1 + compiler: clang + env: [ HASKELL=ON ] + - os: linux compiler: gcc @@ -59,7 +66,7 @@ before_install: brew install ninja fi - | - if [[ "$TRAVIS_OS_NAME" == "osx" && "$FAST" != "ON" ]]; then + if [[ "$TRAVIS_OS_NAME" == "osx" && "$FAST" != "ON" && "$HASKELL" != "ON" ]]; then rvm install 2.3.1 rvm use 2.3.1 gem install test-unit --no-document @@ -76,7 +83,6 @@ before_install: brew install xerces-c fi brew install augeas - brew install cabal-install brew install checkbashisms brew install dbus brew install discount @@ -92,14 +98,16 @@ before_install: brew install yajl pip2 install cheetah # Required by kdb-gen export Qt5_DIR=/usr/local/opt/qt5 - + fi + - | + if [[ "$TRAVIS_OS_NAME" == "osx" && "$HASKELL" == "ON" ]] ; then brew install cabal-install cabal update # avoid reinstalls if we already have them cached # disable everything that slows down compilation times of the dependencies to have reasonable travis build times - which happy || cabal install -disable-optimization --disable-documentation --disable-profiling --disable-tests -j happy - which alex || cabal install -disable-optimization --disable-documentation --disable-profiling --disable-tests -j alex - which c2hs || cabal install -disable-optimization --disable-documentation --disable-profiling --disable-tests -j c2hs + which happy || cabal install happy + which alex || cabal install alex + which c2hs || cabal install c2hs PATH=$PATH:"$HOME/.cabal/bin" fi - | @@ -129,6 +137,11 @@ before_script: if [[ "$TRAVIS_OS_NAME" == "linux" && "$CC" == "clang" || "$TRAVIS_OS_NAME" == "osx" && "${CC:0:3}" == "gcc" ]]; then CMAKE_OPT+=("-DCOMMON_FLAGS=-Werror") fi + # use a minimal configuration for the haskell bindings to give it enough time to compile dependencies + - if [[ $HASKELL == ON ]]; then + bindings="cpp;haskell"; + plugins="resolver_fm_hpu_b;dump;haskell;ini" + fi - | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then python2_ver=$(python2 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') && \ @@ -144,7 +157,7 @@ before_script: -GNinja -DBUILD_STATIC=OFF -DPLUGINS="${plugins:-ALL;-jni}" - -DBINDINGS="${bindings:-ALL;haskell}" + -DBINDINGS="${bindings:-ALL;}" -DENABLE_DEBUG=ON -DTOOLS=ALL -DINSTALL_SYSTEM_FILES=OFF diff --git a/cmake/Modules/LibAddHaskellPlugin.cmake b/cmake/Modules/LibAddHaskellPlugin.cmake index 5fe745e29a6..a699b54bd4a 100644 --- a/cmake/Modules/LibAddHaskellPlugin.cmake +++ b/cmake/Modules/LibAddHaskellPlugin.cmake @@ -160,7 +160,8 @@ macro (add_haskell_plugin target) OUTPUT ${PLUGIN_HASKELL_NAME} # this way it will generate predictable output filenames # and compile the haskell part of this plugin with cabal - COMMAND ${CABAL_EXECUTABLE} sandbox init --sandbox "${CMAKE_CURRENT_BINARY_DIR}/../../bindings/haskell/.cabal-sandbox" + COMMAND ${CABAL_EXECUTABLE} sandbox init + COMMAND ${CABAL_EXECUTABLE} sandbox add-source "${CMAKE_CURRENT_BINARY_DIR}/../../bindings/haskell/" # ensure any further dependencies added by plugin developers get installed to the sandbox COMMAND ${CABAL_EXECUTABLE} install --only-dependencies COMMAND ${CABAL_EXECUTABLE} --ipid=${target} ${CABAL_OPTS} configure diff --git a/src/bindings/haskell/CMakeLists.txt b/src/bindings/haskell/CMakeLists.txt index e7bfc1d4691..c7ee73048ff 100644 --- a/src/bindings/haskell/CMakeLists.txt +++ b/src/bindings/haskell/CMakeLists.txt @@ -24,7 +24,7 @@ if (NOT BUILD_STATIC) set (BINDING_HASKELL_NAME "${BINDING_HASKELL_NAME}${GHC_DYNAMIC_SUFFIX}${GHC_DYNAMIC_ENDING}") set (CABAL_OPTS "${CABAL_OPTS};--enable-shared") if (BUILD_SHARED) - set (ELEKTRA_DEPENDENCY "elektra;elektra-kdb;") + set (ELEKTRA_DEPENDENCY "elektra;elektra-kdb;elektra-ease;") elseif (BUILD_FULL) set (ELEKTRA_DEPENDENCY "elektra-full;") endif () @@ -47,7 +47,7 @@ if (NOT BUILD_STATIC) COMMAND ${CABAL_EXECUTABLE} sandbox init COMMAND ${CABAL_EXECUTABLE} --ipid=libelektra-haskell-${KDB_VERSION} ${CABAL_OPTS} configure # disable everything that slows down compilation times of the dependencies to have reasonable travis build times - COMMAND ${CABAL_EXECUTABLE} install --disable-optimization --disable-documentation --disable-profiling --disable-tests -j --only-dependencies + COMMAND ${CABAL_EXECUTABLE} install --only-dependencies # this only installs to the sandbox so plugins can depend on it # without breaking the global db through shared sandboxes COMMAND ${CABAL_EXECUTABLE} build @@ -58,6 +58,7 @@ if (NOT BUILD_STATIC) "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/KeySet.chs" "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/Plugin.chs" "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/KDB.chs" + "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/Ease.chs" "${CMAKE_CURRENT_SOURCE_DIR}/test/Elektra.hs" "${CMAKE_CURRENT_SOURCE_DIR}/test/ElektraRealWorld.hs" ${ELEKTRA_DEPENDENCY} @@ -88,7 +89,7 @@ if (NOT BUILD_STATIC) OUTPUT ${HASKELL_TESTS} # they are getting installed to the sandbox, and they get cached to avoid reinstalling every time # disable everything that slows down compilation times of the dependencies to have reasonable travis build times - COMMAND ${CABAL_EXECUTABLE} install --disable-optimization --disable-documentation --disable-profiling -j --enable-tests --only-dependencies + COMMAND ${CABAL_EXECUTABLE} install --enable-tests --only-dependencies COMMAND ${CABAL_EXECUTABLE} --ipid=libelektra-haskell-${KDB_VERSION} ${CABAL_OPTS} --enable-tests configure COMMAND ${CABAL_EXECUTABLE} build WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} @@ -97,6 +98,7 @@ if (NOT BUILD_STATIC) "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/KeySet.chs" "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/Plugin.chs" "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/KDB.chs" + "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/Ease.chs" "${CMAKE_CURRENT_SOURCE_DIR}/test/Elektra.hs" "${CMAKE_CURRENT_SOURCE_DIR}/test/ElektraRealWorld.hs" ${ELEKTRA_DEPENDENCY} diff --git a/src/bindings/haskell/libelektra-haskell.cabal.in b/src/bindings/haskell/libelektra-haskell.cabal.in index 8f6b74e7df3..493bc551d5a 100644 --- a/src/bindings/haskell/libelektra-haskell.cabal.in +++ b/src/bindings/haskell/libelektra-haskell.cabal.in @@ -17,8 +17,9 @@ library , Elektra.KeySet , Elektra.KDB , Elektra.Plugin + , Elektra.Ease build-depends: base >= 4.7 && < 5 - includes: kdb.h, kdbplugin.h + includes: kdb.h, kdbplugin.h, kdbease.h include-dirs: @CABAL_INCLUDE_DIRS@ extra-lib-dirs: "@CMAKE_BINARY_DIR@/lib" default-language: Haskell2010 diff --git a/src/bindings/haskell/src/Elektra/Ease.chs b/src/bindings/haskell/src/Elektra/Ease.chs new file mode 100644 index 00000000000..e03adb2635a --- /dev/null +++ b/src/bindings/haskell/src/Elektra/Ease.chs @@ -0,0 +1,9 @@ +module Elektra.Ease (keyGetRelativeName) where + +#include + +{#import Elektra.Key#} + +{#context lib="libelektra" #} + +{#fun unsafe elektraKeyGetRelativeName as keyGetRelativeName {`Key', `Key'} -> `String' #} From a445dadc05b19cabbf0d7e92902a799203a55c76 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Wed, 29 Nov 2017 21:16:12 +0100 Subject: [PATCH 10/36] haskell-cabal-sandboxing: the usual travis fix attempts --- .travis.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9a7b17923e1..c0d7eabec55 100644 --- a/.travis.yml +++ b/.travis.yml @@ -138,10 +138,8 @@ before_script: CMAKE_OPT+=("-DCOMMON_FLAGS=-Werror") fi # use a minimal configuration for the haskell bindings to give it enough time to compile dependencies - - if [[ $HASKELL == ON ]]; then - bindings="cpp;haskell"; - plugins="resolver_fm_hpu_b;dump;haskell;ini" - fi + - if [[ $HASKELL == ON ]]; then bindings="cpp;haskell"; fi + - if [[ $HASKELL == ON ]]; then plugins="resolver_fm_hpu_b;dump;haskell;ini"; fi - | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then python2_ver=$(python2 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') && \ From 210065111f386be77503ad71572f4fe188e379bc Mon Sep 17 00:00:00 2001 From: e1528532 Date: Tue, 12 Dec 2017 20:29:35 +0100 Subject: [PATCH 11/36] haskell-cabal-sandboxing: add array function bindings for haskell to kdbease --- src/bindings/haskell/src/Elektra/Ease.chs | 15 +++- src/bindings/haskell/src/Elektra/Key.chs | 96 ++++++++++++++--------- src/libs/ease/array.c | 1 - 3 files changed, 74 insertions(+), 38 deletions(-) diff --git a/src/bindings/haskell/src/Elektra/Ease.chs b/src/bindings/haskell/src/Elektra/Ease.chs index e03adb2635a..eb8e14fd427 100644 --- a/src/bindings/haskell/src/Elektra/Ease.chs +++ b/src/bindings/haskell/src/Elektra/Ease.chs @@ -1,9 +1,22 @@ -module Elektra.Ease (keyGetRelativeName) where +module Elektra.Ease (ArrayValidateNameResult (..), keyGetRelativeName, arrayValidateName, arrayIncName, arrayGet, arrayGetNextKey) where #include {#import Elektra.Key#} +{#import Elektra.KeySet#} {#context lib="libelektra" #} {#fun unsafe elektraKeyGetRelativeName as keyGetRelativeName {`Key', `Key'} -> `String' #} + +data ArrayValidateNameResult = Invalid | Start | Element deriving (Show, Eq, Enum) + +arrayValidateName = fmap parseResult . elektraArrayValidateName + where + parseResult 0 = Start + parseResult 1 = Element + parseResult _ = Invalid +{#fun unsafe elektraArrayValidateName {`Key'} -> `Int' #} +{#fun unsafe elektraArrayIncName as arrayIncName {`Key'} -> `Int' #} +{#fun unsafe elektraArrayGet as arrayGet {`Key', `KeySet'} -> `KeySet' #} +{#fun unsafe elektraArrayGetNextKey as arrayGetNextKey {`KeySet'} -> `Key' #} diff --git a/src/bindings/haskell/src/Elektra/Key.chs b/src/bindings/haskell/src/Elektra/Key.chs index 2138a5d166f..3e6fb60ece3 100644 --- a/src/bindings/haskell/src/Elektra/Key.chs +++ b/src/bindings/haskell/src/Elektra/Key.chs @@ -1,7 +1,7 @@ -module Elektra.Key (Key (..), Namespace (..), withKey, - keyNew, keyNewWithValue, keyDup, keyCopy, keyClear, keyIncRef, keyDecRef, keyGetRef, +module Elektra.Key (Key (..), Namespace (..), withKey, ElektraKeyVarargs (KeyMetaName, KeyBinary, KeyComment, KeyOwner), + keyNew, keyNewWithValue, keyNewWithFlagsAndValue, keyDup, keyCopy, keyClear, keyIncRef, keyDecRef, keyGetRef, keyName, keyGetNameSize, keyUnescapedName, keyGetUnescapedNameSize, keySetName, keyGetFullNameSize, keyGetFullName, - keyAddName, keyBaseName, keyGetBaseName, keyGetBaseNameSize, keyAddBaseName, keySetBaseName, keyGetNamespace, + keyAddName, keyBaseName, keyGetBaseName, keyGetBaseNameSize, keyAddBaseName, keySetBaseName, keyDeleteBaseName, keyGetNamespace, keyString, keyGetValueSize, keySetString, keySet, keyRewindMeta, keyNextMeta, keyCurrentMeta, keyCopyMeta, keyCopyAllMeta, keyGetMeta, keySetMeta, keyListMeta, keyCmp, keyNeedSync, keyIsBelow, keyIsDirectBelow, keyRel, keyIsInactive, keyIsBinary, keyIsString, keyPtrNull, ifKey) where @@ -11,6 +11,7 @@ import Foreign.Marshal.Alloc (allocaBytes) import Foreign.Ptr (castPtr, nullPtr) import Foreign.ForeignPtr (withForeignPtr) import System.IO.Unsafe (unsafePerformIO) +import Data.Maybe (isJust, fromJust) import Control.Monad (liftM) {#context lib="libelektra" #} @@ -27,6 +28,13 @@ import Control.Monad (liftM) {#enum KEY_NS_NONE as Namespace { underscoreToCase } deriving (Show, Eq) #} {#enum KEY_NAME as ElektraKeyVarargs { underscoreToCase } deriving (Show, Eq) #} +data KeyNew = KeyNew { knKeyName :: Maybe String + , knKeyValue :: Maybe String + , knMeta :: [(String, String)] + , knKeySize :: Maybe Int + , knFlags :: [ElektraKeyVarargs] + } deriving (Show) + -- *** -- KEY CREATION / DELETION / COPPY METHODS -- *** @@ -38,22 +46,29 @@ keyPtrNull (Key ptr) = withForeignPtr ptr (return . (== nullPtr)) -- so it gets deleted properly when haskell calls the finalizer keyNew :: String -> IO Key keyNew name = do - key <- keyNewRaw name KeyEnd - keyIncRef key - return key + key <- keyNewRaw name KeyEnd + keyIncRef key + return key keyNewWithValue :: String -> String -> IO Key keyNewWithValue name value = do - key <- keyNewRawWithValue name KeyValue value KeyEnd - keyIncRef key - return key + key <- keyNewRawWithValue name KeyValue value KeyEnd + keyIncRef key + return key +keyNewWithFlagsAndValue :: String -> ElektraKeyVarargs -> String -> IO Key +keyNewWithFlagsAndValue name flags value = do + key <- keyNewRawWithFlagsAndValue name KeyFlags flags KeyValue value KeyEnd + keyIncRef key + return key {#fun unsafe variadic keyNew[keyswitch_t] as keyNewRaw {`String', `ElektraKeyVarargs'} -> `Key' #} {#fun unsafe variadic keyNew[keyswitch_t, const char *, keyswitch_t] - as keyNewRawWithValue {`String', `ElektraKeyVarargs', `String', `ElektraKeyVarargs'} -> `Key' #} + as keyNewRawWithValue {`String', `ElektraKeyVarargs', `String', `ElektraKeyVarargs'} -> `Key' #} +{#fun unsafe variadic keyNew[keyswitch_t, keyswitch_t, keyswitch_t, const char *, keyswitch_t] + as keyNewRawWithFlagsAndValue {`String', `ElektraKeyVarargs', `ElektraKeyVarargs', `ElektraKeyVarargs', `String', `ElektraKeyVarargs'} -> `Key' #} keyDup :: Key -> IO Key keyDup key = do - dup <- keyDupRaw key - keyIncRef dup - return dup + dup <- keyDupRaw key + keyIncRef dup + return dup {#fun unsafe keyDup as keyDupRaw {`Key'} -> `Key' #} {#fun unsafe keyCopy {`Key', `Key'} -> `Int' #} {#fun unsafe keyClear {`Key'} -> `Int' #} @@ -70,19 +85,19 @@ keyGetName = keyName {#fun unsafe keyGetNameSize {`Key'} -> `Int' #} keyUnescapedName :: Key -> IO String keyUnescapedName key = do - size <- keyGetUnescapedNameSize key - withKey key $ (\cKey -> do - result <- {#call unsafe keyUnescapedName as keyUnescapedNameRaw #} cKey - C2HSImp.peekCStringLen (castPtr result, size)) + size <- keyGetUnescapedNameSize key + withKey key $ (\cKey -> do + result <- {#call unsafe keyUnescapedName as keyUnescapedNameRaw #} cKey + C2HSImp.peekCStringLen (castPtr result, size)) {#fun unsafe keyGetUnescapedNameSize {`Key'} -> `Int' #} {#fun unsafe keySetName {`Key', `String'} -> `Int' #} keyGetFullName :: (Key) -> IO String keyGetFullName key = do - size <- keyGetFullNameSize key - withKey key $ \cKey -> - allocaBytes size (\result -> do - {#call unsafe keyGetFullName as keyGetFullNameRaw #} cKey result size - C2HSImp.peekCString result) + size <- keyGetFullNameSize key + withKey key $ \cKey -> + allocaBytes size (\result -> do + {#call unsafe keyGetFullName as keyGetFullNameRaw #} cKey result size + C2HSImp.peekCString result) {#fun unsafe keyGetFullNameSize {`Key'} -> `Int' #} {#fun unsafe keyBaseName {`Key'} -> `String' #} keyGetBaseName :: Key -> IO String @@ -90,7 +105,16 @@ keyGetBaseName = keyBaseName {#fun unsafe keyGetBaseNameSize {`Key'} -> `Int' #} {#fun unsafe keyAddBaseName {`Key', `String'} -> `Int' #} {#fun unsafe keyAddName {`Key', `String'} -> `Int' #} -{#fun unsafe keySetBaseName {`Key', `String'} -> `Int' #} +keySetBaseName :: Key -> String -> IO Int +keySetBaseName key baseName = keySetBaseNameRaw key (Just baseName) +keyDeleteBaseName :: Key -> IO Int +keyDeleteBaseName key = keySetBaseNameRaw key Nothing +keySetBaseNameRaw :: Key -> Maybe String -> IO Int +keySetBaseNameRaw key baseName = do + withKey key $ \cKey -> if (isJust baseName) + then C2HSImp.withCString (fromJust baseName) $ \cValue -> call cKey cValue + else call cKey nullPtr + where call cKey cValue = {#call unsafe keySetBaseName as keySetBaseNameRaw' #} cKey cValue {#fun unsafe keyGetNamespace {`Key'} -> `Namespace' #} -- *** @@ -118,11 +142,11 @@ keySet key = keySetString key . show {#fun unsafe keySetMeta {`Key', `String', `String'} -> `Int' #} keyListMeta :: Key -> IO [Key] keyListMeta key = keyRewindMeta key >> listMeta [] - where - listMeta res = do - cur <- keyNextMeta key - isNull <- keyPtrNull cur - if isNull then return res else keyIncRef cur >> liftM (cur :) (listMeta res) + where + listMeta res = do + cur <- keyNextMeta key + isNull <- keyPtrNull cur + if isNull then return res else keyIncRef cur >> liftM (cur :) (listMeta res) -- *** -- KEY TESTING METHODS @@ -143,14 +167,14 @@ keyListMeta key = keyRewindMeta key >> listMeta [] -- *** instance Show Key where - show key = unsafePerformIO $ do - name <- keyName key - value <- keyString key - ref <- keyGetRef key - return $ name ++ " " ++ value ++ " " ++ (show ref) + show key = unsafePerformIO $ do + name <- keyName key + value <- keyString key + ref <- keyGetRef key + return $ name ++ " " ++ value ++ " " ++ (show ref) instance Eq Key where - key1 == key2 = unsafePerformIO $ fmap (== 0) $ keyCmp key1 key2 + key1 == key2 = unsafePerformIO $ fmap (== 0) $ keyCmp key1 key2 -- *** -- ADDITIONAL HELPERS USEFUL IN HASKELL @@ -158,5 +182,5 @@ instance Eq Key where ifKey :: IO Key -> (Key -> IO a) -> IO a -> IO a ifKey k t f = do - null <- k >>= keyPtrNull - if null then f else k >>= t + null <- k >>= keyPtrNull + if null then f else k >>= t diff --git a/src/libs/ease/array.c b/src/libs/ease/array.c index 2b10aa3560d..8b95069f439 100644 --- a/src/libs/ease/array.c +++ b/src/libs/ease/array.c @@ -21,7 +21,6 @@ #include /** - * @internal * @brief validate array syntax * * @param key an element of an array From aedfe1e0afb7efab9ac1553427cd217a3bd03f59 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Tue, 12 Dec 2017 20:30:17 +0100 Subject: [PATCH 12/36] haskell-cabal-sandboxing: workaround the ghc ffi issue --- src/plugins/haskell/haskell.c.in | 41 +++++++++++++++++++++++++-- src/plugins/haskell/testmod_haskell.c | 2 ++ tests/kdb/testkdb_allplugins.cpp | 1 + 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/plugins/haskell/haskell.c.in b/src/plugins/haskell/haskell.c.in index a52e1b2ffd6..0bc2e3cc02a 100644 --- a/src/plugins/haskell/haskell.c.in +++ b/src/plugins/haskell/haskell.c.in @@ -12,14 +12,41 @@ #include "@PLUGIN_NAME_CAPITALIZED@_stub.h" #include "haskell.h" +// Just to ignore the warnings from RTS.h, once the GHC bug mentioned below is +// fixed we don't need this anymore, hs_exit is included in the stub. +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wflexible-array-extensions" +#pragma clang diagnostic ignored "-Wundef" +#endif +#ifdef __GCC__ +#pragma gcc diagnostic push +#pragma gcc diagnostic ignored "-Wflexible-array-extensions" +#pragma gcc diagnostic ignored "-Wundef" +#endif + +#include "Rts.h" + +#ifdef __GCC__ +#pragma gcc diagnostic pop +#endif +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + #include +static int started = 0; + int elektraHaskellOpen (Plugin * handle, Key * errorKey) { static char *argv[] = { "@PLUGIN_NAME@", 0 }, **argvPtr = argv; static int argc = 1; // Startup the haskell runtime with some dummy args - hs_init (&argc, &argvPtr); + if (!started) { + hs_init (&argc, &argvPtr); + started++; + } return hs_elektraHaskellOpen (handle, errorKey); } @@ -27,7 +54,17 @@ int elektraHaskellClose (Plugin * handle, Key * errorKey) { int ret = hs_elektraHaskellClose (handle, errorKey); // Shutdown the haskell runtime - hs_exit (); + // Due to an unfortunate bug within GHC's ffi implementation we cannot + // restart the runtime if it got closed once during elektra's runtime. + // This usecase can happen though especially with dynamic plugins. + // As a temporary workaround we leave it open for now and just call a cleanup function + // to minimize resource usage while its not needed, as we can't guarantee we + // will never load it again without restarting elektra as a whole + // https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/bugs.html#infelicities-ffi + // hs_exit (); + if (!--started) { + rts_done(); + } return ret; } diff --git a/src/plugins/haskell/testmod_haskell.c b/src/plugins/haskell/testmod_haskell.c index 1c7edd13a89..c89e74e3f46 100644 --- a/src/plugins/haskell/testmod_haskell.c +++ b/src/plugins/haskell/testmod_haskell.c @@ -47,6 +47,8 @@ int main (int argc, char ** argv) init (argc, argv); + test_basics (); + // Test again to check loading/unloading the haskell plugin works test_basics (); print_result ("testmod_haskell"); diff --git a/tests/kdb/testkdb_allplugins.cpp b/tests/kdb/testkdb_allplugins.cpp index cf1ea6fe0c5..b46313a967f 100644 --- a/tests/kdb/testkdb_allplugins.cpp +++ b/tests/kdb/testkdb_allplugins.cpp @@ -29,6 +29,7 @@ std::vector getAllPlugins () std::vector plugins = mpd.listAllPlugins (); // remove known problems + plugins.erase (std::remove (plugins.begin (), plugins.end (), "haskell"), plugins.end ()); plugins.erase (std::remove (plugins.begin (), plugins.end (), "xerces"), plugins.end ()); plugins.erase (std::remove (plugins.begin (), plugins.end (), "ruby"), plugins.end ()); plugins.erase (std::remove (plugins.begin (), plugins.end (), "jni"), plugins.end ()); From dd1692ec6d920356ea5db5be0bc6d5cf6c7d6f6c Mon Sep 17 00:00:00 2001 From: e1528532 Date: Thu, 14 Dec 2017 21:08:08 +0100 Subject: [PATCH 13/36] haskell-cabal-sandboxing: reformat, adjust travis plugins for haskell build --- .travis.yml | 2 +- src/plugins/haskell/haskell.c.in | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index c0d7eabec55..a84c865674a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -139,7 +139,7 @@ before_script: fi # use a minimal configuration for the haskell bindings to give it enough time to compile dependencies - if [[ $HASKELL == ON ]]; then bindings="cpp;haskell"; fi - - if [[ $HASKELL == ON ]]; then plugins="resolver_fm_hpu_b;dump;haskell;ini"; fi + - if [[ $HASKELL == ON ]]; then plugins="resolver_fm_hpu_b;dump;haskell;ini;sync;error;list;ipaddr;hosts"; fi - | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then python2_ver=$(python2 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') && \ diff --git a/src/plugins/haskell/haskell.c.in b/src/plugins/haskell/haskell.c.in index 0bc2e3cc02a..20f80b1c801 100644 --- a/src/plugins/haskell/haskell.c.in +++ b/src/plugins/haskell/haskell.c.in @@ -43,7 +43,8 @@ int elektraHaskellOpen (Plugin * handle, Key * errorKey) static char *argv[] = { "@PLUGIN_NAME@", 0 }, **argvPtr = argv; static int argc = 1; // Startup the haskell runtime with some dummy args - if (!started) { + if (!started) + { hs_init (&argc, &argvPtr); started++; } @@ -62,7 +63,8 @@ int elektraHaskellClose (Plugin * handle, Key * errorKey) // will never load it again without restarting elektra as a whole // https://downloads.haskell.org/~ghc/8.2.2/docs/html/users_guide/bugs.html#infelicities-ffi // hs_exit (); - if (!--started) { + if (!--started) + { rts_done(); } return ret; From 59146636ff60ccb78f4d3c1fff9837e4a403820e Mon Sep 17 00:00:00 2001 From: e1528532 Date: Thu, 14 Dec 2017 23:51:57 +0100 Subject: [PATCH 14/36] haskell-cabal-sandboxing: format c file, add shared sandbox support to speed up compilation --- cmake/Modules/LibAddHaskellPlugin.cmake | 19 +++++++++++++++---- src/bindings/haskell/CMakeLists.txt | 2 +- src/plugins/haskell/haskell.c.in | 2 +- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/cmake/Modules/LibAddHaskellPlugin.cmake b/cmake/Modules/LibAddHaskellPlugin.cmake index a699b54bd4a..78f78ec609e 100644 --- a/cmake/Modules/LibAddHaskellPlugin.cmake +++ b/cmake/Modules/LibAddHaskellPlugin.cmake @@ -10,11 +10,18 @@ include (LibAddBinding) # MODULES: # the name of the haskell modules to be compiled # by default it assumes there is a single module called Elektra. +# NO_SHARED_SANDBOX: +# By default all haskell plugins and the bindings are compiled in a shared sandbox to +# peed up compilation times by only compiling commonly-used libraries once. Set this +# flag to use an independent sandbox instead in case there are e.g. library version conflicts +# SANDBOX_ADD_SOURCES: +# additional source paths which should be added to the cabal sandbox +# required if the build should depend on haskell libraries not available on hackage macro (add_haskell_plugin target) cmake_parse_arguments (ARG - "" # optional keywords + "NO_SHARED_SANDBOX" # optional keywords "MODULE" # one value keywords - "MODULES" # multi value keywords + "MODULES;SANDBOX_ADD_SOURCES" # multi value keywords ${ARGN} ) @@ -156,12 +163,16 @@ macro (add_haskell_plugin target) DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" ) + if (NOT ARG_NO_SHARED_SANDBOX) + set (SHARED_SANDBOX "--sandbox;\"${CMAKE_BINARY_DIR}/.cabal-sandbox\"") + endif (NOT ARG_NO_SHARED_SANDBOX) + add_custom_command ( OUTPUT ${PLUGIN_HASKELL_NAME} # this way it will generate predictable output filenames # and compile the haskell part of this plugin with cabal - COMMAND ${CABAL_EXECUTABLE} sandbox init - COMMAND ${CABAL_EXECUTABLE} sandbox add-source "${CMAKE_CURRENT_BINARY_DIR}/../../bindings/haskell/" + COMMAND ${CABAL_EXECUTABLE} sandbox init ${SHARED_SANDBOX} + COMMAND ${CABAL_EXECUTABLE} sandbox add-source "${CMAKE_CURRENT_BINARY_DIR}/../../bindings/haskell/" ${ARG_SANDBOX_ADD_SOURCES} # ensure any further dependencies added by plugin developers get installed to the sandbox COMMAND ${CABAL_EXECUTABLE} install --only-dependencies COMMAND ${CABAL_EXECUTABLE} --ipid=${target} ${CABAL_OPTS} configure diff --git a/src/bindings/haskell/CMakeLists.txt b/src/bindings/haskell/CMakeLists.txt index c7ee73048ff..54c3a71607e 100644 --- a/src/bindings/haskell/CMakeLists.txt +++ b/src/bindings/haskell/CMakeLists.txt @@ -44,7 +44,7 @@ if (NOT BUILD_STATIC) add_custom_command ( OUTPUT ${BINDING_HASKELL_NAME} - COMMAND ${CABAL_EXECUTABLE} sandbox init + COMMAND ${CABAL_EXECUTABLE} sandbox init --sandbox ${CMAKE_BINARY_DIR}/.cabal-sandbox COMMAND ${CABAL_EXECUTABLE} --ipid=libelektra-haskell-${KDB_VERSION} ${CABAL_OPTS} configure # disable everything that slows down compilation times of the dependencies to have reasonable travis build times COMMAND ${CABAL_EXECUTABLE} install --only-dependencies diff --git a/src/plugins/haskell/haskell.c.in b/src/plugins/haskell/haskell.c.in index 20f80b1c801..ca1f8557bd9 100644 --- a/src/plugins/haskell/haskell.c.in +++ b/src/plugins/haskell/haskell.c.in @@ -65,7 +65,7 @@ int elektraHaskellClose (Plugin * handle, Key * errorKey) // hs_exit (); if (!--started) { - rts_done(); + rts_done (); } return ret; } From 360e5f8cc26416a0e072a1ad70334f3aa64031c7 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Thu, 28 Dec 2017 20:18:21 +0100 Subject: [PATCH 15/36] haskell-cabal-sandboxing: fix linking issues by installing haskell dynlibs in ../lib/elektra/haskell and adjusting rpath, add custom Setup.hs post-build logic for plugins and bindings to better glue cmake and cabal together improve sandboxed installation logic to make it more robust and support various haskell dependencies via cabal improved and adjusted readmes --- cmake/Modules/FindHaskell.cmake | 42 +++++- cmake/Modules/LibAddHaskellPlugin.cmake | 99 +++++++++---- src/CMakeLists.txt | 5 +- src/bindings/haskell/CMakeLists.txt | 108 ++++++-------- src/bindings/haskell/README.md | 8 +- src/bindings/haskell/Setup.hs | 2 - .../haskell/libelektra-haskell.cabal.in | 6 +- src/plugins/haskell/README.md | 30 +++- src/plugins/haskell/Setup.hs | 139 ++++++++++++++++++ src/plugins/haskell/haskell.cabal.in | 8 +- src/plugins/haskell/haskell.h.in | 2 +- 11 files changed, 331 insertions(+), 118 deletions(-) delete mode 100644 src/bindings/haskell/Setup.hs create mode 100644 src/plugins/haskell/Setup.hs diff --git a/cmake/Modules/FindHaskell.cmake b/cmake/Modules/FindHaskell.cmake index ebf9e12dcae..1bc12c1f0a1 100644 --- a/cmake/Modules/FindHaskell.cmake +++ b/cmake/Modules/FindHaskell.cmake @@ -8,6 +8,9 @@ # GHC-PKG_EXECUTABLE - Path to the ghc-pkg executable # GHC_HSPEC_FOUND - True if the hspec library is available # GHC_QUICKCHECK_FOUND - True if the QuickCheck library is available +# GHC_VERSION - The numeric version of the ghc executable +# CABAL_DYNLIB_PATH - The default path where cabal installs dynamic libraries +# CABAL_CUSTOM_TARGET - The default dependencies of the custom Setup.hs for plugins # HASKELL_FOUND - True if the whole required haskell environment exists # This variable is set to true if CABAL_EXECUTABLE, C2HS_EXECUTABLE, GHC_EXECUTABLE # and GHC-PKG_EXECUTABLE are all available. If BUILD_TESTING is enabled, it also @@ -25,7 +28,7 @@ if (CABAL_EXECUTABLE) if (C2HS_EXECUTABLE) if (GHC_EXECUTABLE) if (GHC-PKG_EXECUTABLE) - + # check for hspec and QuickCheck # ghc-pkg return code is 0 on success, 1 otherwise execute_process ( @@ -53,11 +56,43 @@ if (GHC-PKG_EXECUTABLE) set (GHC_QUICKCHECK_FOUND 0) endif (GHC_QUICKCHECK_FOUND EQUAL 0) + execute_process ( + COMMAND ${GHC_EXECUTABLE} --numeric-version + OUTPUT_VARIABLE GHC_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + # find the default cabal installation path + # sort of hacky but i haven't found a uniform way of doing this + # first find the global cabal directory + execute_process ( + COMMAND ${CABAL_EXECUTABLE} --ignore-sandbox help + COMMAND tail -n1 + COMMAND xargs dirname + OUTPUT_VARIABLE CABAL_LOCATION OUTPUT_STRIP_TRAILING_WHITESPACE + ) + # filter the library path matching our ghc version, ignoring architectures for now + execute_process ( + COMMAND ls -F "${CABAL_LOCATION}/lib" + COMMAND grep ghc-${GHC_VERSION} + OUTPUT_VARIABLE GHC_DYNAMIC_LIBRARY_DIR OUTPUT_STRIP_TRAILING_WHITESPACE + ) + set (CABAL_DYNLIB_PATH "${CABAL_LOCATION}/lib/${GHC_DYNAMIC_LIBRARY_DIR}") + + # dependencies for the default cmake Setup.hs + set (CABAL_CUSTOM_TARGET +"custom-setup + setup-depends: + Cabal >= 2.0 && < 2.1, + containers >= 0.5 && < 0.6, + base >= 4.7 && < 5 , + directory >= 1.2 && < 1.4, + process >= 1.6 && < 1.7, + filepath >= 1.3 && < 1.5") + # By using cabal sandboxes we can install hspec and QuickCheck to the sandbox without # any concerns as they are independent from the global environment. So they are not required. # All set, have fun with haskell! set (HASKELL_FOUND 1) - else (GHC-PKG_EXECUTABLE) set (HASKELL_NOTFOUND_INFO "ghc-pkg not found") endif (GHC-PKG_EXECUTABLE) @@ -78,6 +113,9 @@ mark_as_advanced ( GHC-PKG_EXECUTABLE C2HS_EXECUTABLE CABAL_EXECUTABLE + CABAL_DYNLIB_PATH + CABAL_CUSTOM_TARGET + GHC_VERSION GHC_HSPEC_FOUND GHC_QUICKCHECK_FOUND ) diff --git a/cmake/Modules/LibAddHaskellPlugin.cmake b/cmake/Modules/LibAddHaskellPlugin.cmake index 78f78ec609e..848e0d3e59e 100644 --- a/cmake/Modules/LibAddHaskellPlugin.cmake +++ b/cmake/Modules/LibAddHaskellPlugin.cmake @@ -1,5 +1,4 @@ include (LibAddMacros) -include (LibAddBinding) # Allows one to add plugins written in haskell, setting up the include paths and # libraries automatically. @@ -17,11 +16,15 @@ include (LibAddBinding) # SANDBOX_ADD_SOURCES: # additional source paths which should be added to the cabal sandbox # required if the build should depend on haskell libraries not available on hackage +# ADDITIONAL_SOURCES: +# in case your plugin depends on other files than *.hs and *.lhs haskell files and the default +# cabal file and c test file and setup file, you can specify them here +# macro (add_haskell_plugin target) cmake_parse_arguments (ARG "NO_SHARED_SANDBOX" # optional keywords "MODULE" # one value keywords - "MODULES;SANDBOX_ADD_SOURCES" # multi value keywords + "MODULES;SANDBOX_ADD_SOURCES;ADDITIONAL_SOURCES" # multi value keywords ${ARGN} ) @@ -37,14 +40,10 @@ macro (add_haskell_plugin target) # set by find_program if (HASKELL_FOUND) - check_binding_included ("haskell" BINDING_WAS_INCLUDED SILENT) - if (BINDING_WAS_INCLUDED) + list (FIND BINDINGS "haskell" FINDEX) + if (FINDEX GREATER -1) # needed for HsFFI.h - execute_process ( - COMMAND ${GHC_EXECUTABLE} --numeric-version - OUTPUT_VARIABLE GHC_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE - ) execute_process ( COMMAND ${GHC_EXECUTABLE} --print-libdir OUTPUT_VARIABLE GHC_LIB_DIR OUTPUT_STRIP_TRAILING_WHITESPACE @@ -112,16 +111,15 @@ macro (add_haskell_plugin target) if (GHC_GMP_LIB) if (GHC_PRIM_LIB) - set (PLUGIN_HASKELL_NAME "${CMAKE_CURRENT_BINARY_DIR}/dist/build/libHS${target}${GHC_DYNAMIC_SUFFIX}${GHC_DYNAMIC_ENDING}") + set (PLUGIN_HASKELL_NAME "${CMAKE_CURRENT_BINARY_DIR}/libHS${target}${GHC_DYNAMIC_SUFFIX}${GHC_DYNAMIC_ENDING}") set (GHC_LIBS - ${PLUGIN_HASKELL_NAME} - "${CMAKE_BINARY_DIR}/src/bindings/haskell/dist/build/libHSlibelektra-haskell-${KDB_VERSION}${GHC_DYNAMIC_SUFFIX}${GHC_DYNAMIC_ENDING}" ${GHC_RTS_LIB} ${GHC_BASE_LIB} ${GHC_GMP_LIB} gmp ${GHC_PRIM_LIB} + ${PLUGIN_HASKELL_NAME} ) # GHC's structure differs between OSX and Linux @@ -162,32 +160,62 @@ macro (add_haskell_plugin target) COPY "${CMAKE_CURRENT_SOURCE_DIR}/README.md" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" ) + # same for the setup logic, depending on wheter a custom one exists + # use the default suitable for almost everything + set (CABAL_CUSTOM_SETUP_FILE "${CMAKE_CURRENT_SOURCE_DIR}/Setup.hs") + if (NOT EXISTS ${CABAL_CUSTOM_SETUP_FILE}) + set (CABAL_CUSTOM_SETUP_FILE "${CMAKE_SOURCE_DIR}/src/plugins/haskell/Setup.hs") + endif (NOT EXISTS ${CABAL_CUSTOM_SETUP_FILE}) + file ( + COPY ${CABAL_CUSTOM_SETUP_FILE} + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" + ) + set (SANDBOX_ADD_SOURCES "${ARG_SANDBOX_ADD_SOURCES};../../bindings/haskell/") if (NOT ARG_NO_SHARED_SANDBOX) - set (SHARED_SANDBOX "--sandbox;\"${CMAKE_BINARY_DIR}/.cabal-sandbox\"") + set (SHARED_SANDBOX "--sandbox;${CMAKE_BINARY_DIR}/.cabal-sandbox") endif (NOT ARG_NO_SHARED_SANDBOX) + execute_process (COMMAND ${CABAL_EXECUTABLE} sandbox init ${SHARED_SANDBOX} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} OUTPUT_QUIET) + if (SANDBOX_ADD_SOURCES) + foreach (SANDBOX_ADD_SOURCE ${SANDBOX_ADD_SOURCES}) + execute_process (COMMAND ${CABAL_EXECUTABLE} sandbox add-source "${SANDBOX_ADD_SOURCE}" + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + endforeach() + endif (SANDBOX_ADD_SOURCES) + + # Grab potential haskell source files + file (GLOB_RECURSE PLUGIN_SOURCE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/*.hs" + "${CMAKE_CURRENT_SOURCE_DIR}/*.lhs" + ) + set (PLUGIN_SOURCE_FILES + "${PLUGIN_SOURCE_FILES}" + "${CMAKE_CURRENT_SOURCE_DIR}/${target}.cabal.in" + "${CMAKE_CURRENT_SOURCE_DIR}/testmod_${target}.c" + "${CMAKE_SOURCE_DIR}/src/plugins/haskell/Setup.hs" + "${ARG_ADDITIONAL_SOURCES}" + ) + add_custom_command ( OUTPUT ${PLUGIN_HASKELL_NAME} - # this way it will generate predictable output filenames - # and compile the haskell part of this plugin with cabal - COMMAND ${CABAL_EXECUTABLE} sandbox init ${SHARED_SANDBOX} - COMMAND ${CABAL_EXECUTABLE} sandbox add-source "${CMAKE_CURRENT_BINARY_DIR}/../../bindings/haskell/" ${ARG_SANDBOX_ADD_SOURCES} # ensure any further dependencies added by plugin developers get installed to the sandbox COMMAND ${CABAL_EXECUTABLE} install --only-dependencies - COMMAND ${CABAL_EXECUTABLE} --ipid=${target} ${CABAL_OPTS} configure + COMMAND ${CABAL_EXECUTABLE} ${CABAL_OPTS} configure COMMAND ${CABAL_EXECUTABLE} build WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS c2hs_haskell - "${CMAKE_SOURCE_DIR}/src/plugins/haskell/Elektra/Haskell.hs" + "${CMAKE_SOURCE_DIR}/src/plugins/haskell/haskell.c.in" + "${CMAKE_SOURCE_DIR}/src/plugins/haskell/haskell.h.in" + "${CMAKE_SOURCE_DIR}/src/plugins/haskell/Setup.hs" + ${PLUGIN_SOURCE_FILES} ) - add_custom_target (${target} DEPENDS ${PLUGIN_HASKELL_NAME}) + add_custom_target (${target} ALL DEPENDS ${PLUGIN_HASKELL_NAME}) + if (BUILD_SHARED OR BUILD_FULL) - add_custom_command(TARGET ${target} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - "${PLUGIN_HASKELL_NAME}" - "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/" - ) + install (DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/haskell" + DESTINATION "lib${LIB_SUFFIX}/elektra/") endif (BUILD_SHARED OR BUILD_FULL) else (GHC_PRIM_LIB) @@ -203,9 +231,9 @@ macro (add_haskell_plugin target) remove_plugin (${target} "GHC_RTS_LIB not found") endif (GHC_RTS_LIB) - else (BINDING_WAS_INCLUDED) + else (FINDEX GREATER -1) remove_plugin (${target} "haskell bindings are not included in the cmake configuration") - endif (BINDING_WAS_INCLUDED) + endif (FINDEX GREATER -1) else (HASKELL_FOUND) remove_plugin (${target} ${HASKELL_NOTFOUND_INFO}) endif (HASKELL_FOUND) @@ -215,7 +243,7 @@ macro (add_haskell_plugin target) # the actual haskell plugin gets linked in dynamically as a library add_plugin (${target} SOURCES - ${CMAKE_SOURCE_DIR}/src/plugins/haskell/haskell.h + ${CMAKE_CURRENT_BINARY_DIR}/haskell.h ${CMAKE_CURRENT_BINARY_DIR}/haskell.c INCLUDE_DIRECTORIES ${GHC_INCLUDE_DIRS} @@ -225,9 +253,20 @@ macro (add_haskell_plugin target) ${target} c2hs_haskell ADD_TEST ) - if (ADDTESTING_PHASE AND BUILD_TESTING) - set_property (TEST testmod_${target} PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib") - endif (ADDTESTING_PHASE AND BUILD_TESTING) + + if (DEPENDENCY_PHASE AND (BUILD_SHARED OR BUILD_FULL)) + set_target_properties (elektra-${target} + PROPERTIES + INSTALL_RPATH "${CMAKE_INSTALL_RPATH};${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}/elektra/haskell") + endif (DEPENDENCY_PHASE AND (BUILD_SHARED OR BUILD_FULL)) + if (ADDTESTING_PHASE AND BUILD_TESTING AND (BUILD_SHARED OR BUILD_FULL)) + set_target_properties (testmod_${target} + PROPERTIES + INSTALL_RPATH "${CMAKE_INSTALL_RPATH};${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}/elektra/haskell") + # guide the tests to our haskell libraries + set_property (TEST testmod_${target} PROPERTY ENVIRONMENT + "LD_LIBRARY_PATH=${CMAKE_CURRENT_BINARY_DIR}/haskell") + endif (ADDTESTING_PHASE AND BUILD_TESTING AND (BUILD_SHARED OR BUILD_FULL)) mark_as_advanced ( GHC_FFI_LIB diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 75de79a781e..cb475eac63b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,12 +1,11 @@ add_subdirectory (error) - set (ADDTESTING_PHASE OFF) -add_subdirectory (plugins) +add_subdirectory (bindings) add_subdirectory (libs) -add_subdirectory (bindings) +add_subdirectory (plugins) add_subdirectory (tools) diff --git a/src/bindings/haskell/CMakeLists.txt b/src/bindings/haskell/CMakeLists.txt index 54c3a71607e..8ab6f7e5f70 100644 --- a/src/bindings/haskell/CMakeLists.txt +++ b/src/bindings/haskell/CMakeLists.txt @@ -1,18 +1,11 @@ find_package (Haskell) -if (HASKELL_FOUND) +if (HASKELL_FOUND) if (NOT BUILD_STATIC) - add_binding (haskell) - set (CABAL_INCLUDE_DIRS "\"${CMAKE_SOURCE_DIR}/src/include\", \"${CMAKE_BINARY_DIR}/src/include\"") - execute_process ( - COMMAND ${GHC_EXECUTABLE} --numeric-version - OUTPUT_VARIABLE GHC_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE - ) - - set (BINDING_HASKELL_NAME "${CMAKE_CURRENT_BINARY_DIR}/dist/build/libHSlibelektra-haskell-${KDB_VERSION}") + set (BINDING_HASKELL_NAME "${CMAKE_CURRENT_BINARY_DIR}/libHSlibelektra-haskell") set (CABAL_OPTS "--prefix=${CMAKE_INSTALL_PREFIX}") if (BUILD_SHARED OR BUILD_FULL) set (GHC_DYNAMIC_SUFFIX "-ghc${GHC_VERSION}") @@ -34,52 +27,56 @@ if (NOT BUILD_STATIC) set (ELEKTRA_DEPENDENCY "elektra-static;") endif () string (REPLACE ";" " " CABAL_ELEKTRA_DEPENDENCY "${ELEKTRA_DEPENDENCY}") - + # configure include paths configure_file ( "${CMAKE_CURRENT_SOURCE_DIR}/libelektra-haskell.cabal.in" "${CMAKE_CURRENT_BINARY_DIR}/libelektra-haskell.cabal" @ONLY ) + # Use the post-build logic to glue the bindings together with cmake as its done for plugins + file ( + COPY "${CMAKE_SOURCE_DIR}/src/plugins/haskell/Setup.hs" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" + ) + set (BINDING_HASKELL_DEPENDENCIES + "${CMAKE_CURRENT_SOURCE_DIR}/libelektra-haskell.cabal.in" + "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/Key.chs" + "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/KeySet.chs" + "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/Plugin.chs" + "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/KDB.chs" + "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/Ease.chs" + "${CMAKE_CURRENT_SOURCE_DIR}/test/Elektra.hs" + "${CMAKE_CURRENT_SOURCE_DIR}/test/ElektraRealWorld.hs" + "${CMAKE_SOURCE_DIR}/src/plugins/haskell/Setup.hs" + ) + execute_process (COMMAND ${CABAL_EXECUTABLE} sandbox init --sandbox ${CMAKE_BINARY_DIR}/.cabal-sandbox + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} OUTPUT_QUIET) add_custom_command ( OUTPUT ${BINDING_HASKELL_NAME} - COMMAND ${CABAL_EXECUTABLE} sandbox init --sandbox ${CMAKE_BINARY_DIR}/.cabal-sandbox - COMMAND ${CABAL_EXECUTABLE} --ipid=libelektra-haskell-${KDB_VERSION} ${CABAL_OPTS} configure - # disable everything that slows down compilation times of the dependencies to have reasonable travis build times COMMAND ${CABAL_EXECUTABLE} install --only-dependencies + COMMAND ${CABAL_EXECUTABLE} ${CABAL_OPTS} configure # this only installs to the sandbox so plugins can depend on it # without breaking the global db through shared sandboxes COMMAND ${CABAL_EXECUTABLE} build - COMMAND ${CABAL_EXECUTABLE} install + # COMMAND ${CABAL_EXECUTABLE} install WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/libelektra-haskell.cabal" - "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/Key.chs" - "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/KeySet.chs" - "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/Plugin.chs" - "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/KDB.chs" - "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/Ease.chs" - "${CMAKE_CURRENT_SOURCE_DIR}/test/Elektra.hs" - "${CMAKE_CURRENT_SOURCE_DIR}/test/ElektraRealWorld.hs" - ${ELEKTRA_DEPENDENCY} + DEPENDS ${BINDING_HASKELL_DEPENDENCIES} ${ELEKTRA_DEPENDENCY} ) add_custom_target (c2hs_haskell ALL DEPENDS "${BINDING_HASKELL_NAME}") if (BUILD_SHARED OR BUILD_FULL) - add_custom_command (TARGET c2hs_haskell POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - "${BINDING_HASKELL_NAME}" - "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/" - ) + # install (FILES "${BINDING_HASKELL_NAME}" DESTINATION lib${LIB_SUFFIX}) + # build and install it to the global db + # install (CODE "execute_process (COMMAND ${CABAL_EXECUTABLE} --ignore-sandbox install WORKING_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}\")") endif (BUILD_SHARED OR BUILD_FULL) - # build and install it to the global db - install (CODE "execute_process (COMMAND ${CABAL_EXECUTABLE} --ignore-sandbox install WORKING_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}\")") if (BUILD_TESTING) # recompile with tests enabled, to get the dependency graph for the static versions correct # the tests need the elektra library already built - while for the haskell plugins it doesn't matter # as a static build depends on the plugins but not on the bindings, this is the way we can resolve the # circular dependency by treating the tests separately - set (HASKELL_TESTS + set (HASKELL_TESTS "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_basic/testhaskell_basic" "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_basic_optimized/testhaskell_basic_optimized" "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_realworld/testhaskell_realworld" @@ -90,58 +87,47 @@ if (NOT BUILD_STATIC) # they are getting installed to the sandbox, and they get cached to avoid reinstalling every time # disable everything that slows down compilation times of the dependencies to have reasonable travis build times COMMAND ${CABAL_EXECUTABLE} install --enable-tests --only-dependencies - COMMAND ${CABAL_EXECUTABLE} --ipid=libelektra-haskell-${KDB_VERSION} ${CABAL_OPTS} --enable-tests configure + COMMAND ${CABAL_EXECUTABLE} ${CABAL_OPTS} --enable-tests configure COMMAND ${CABAL_EXECUTABLE} build WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/libelektra-haskell.cabal" - "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/Key.chs" - "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/KeySet.chs" - "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/Plugin.chs" - "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/KDB.chs" - "${CMAKE_CURRENT_SOURCE_DIR}/src/Elektra/Ease.chs" - "${CMAKE_CURRENT_SOURCE_DIR}/test/Elektra.hs" - "${CMAKE_CURRENT_SOURCE_DIR}/test/ElektraRealWorld.hs" - ${ELEKTRA_DEPENDENCY} - c2hs_haskell + DEPENDS ${BINDING_HASKELL_DEPENDENCIES} ${ELEKTRA_DEPENDENCY} c2hs_haskell ) add_custom_target (c2hs_haskell_tests ALL DEPENDS ${HASKELL_TESTS}) + if (INSTALL_TESTING) + # as those are not controlled by cmake really, adjust the rpath manually + # install rpath is enough, we don't depend on any cabal libraries anymore + # tests are statically compiled + foreach (HASKELL_TEST ${HASKELL_TESTS}) + install (CODE "execute_process (COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \"${CMAKE_INSTALL_RPATH}\" \"${HASKELL_TEST}\" OUTPUT_QUIET ERROR_QUIET)") + endforeach (HASKELL_TEST ${HASKELL_TESTS}) + install (FILES ${HASKELL_TESTS} + DESTINATION ${TARGET_TOOL_EXEC_FOLDER} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + endif (INSTALL_TESTING) - # test it using the executables, so cmake takes care about the rpaths - add_test ( - NAME testhaskell_basic - COMMAND "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_basic/testhaskell_basic" - ) + add_test (testhaskell_basic "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_basic/testhaskell_basic") set_property (TEST testhaskell_basic PROPERTY LABELS bindings) set_property (TEST testhaskell_basic PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib") - add_test ( - NAME testhaskell_basic_optimized - COMMAND "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_basic_optimized/testhaskell_basic_optimized" - ) + add_test (testhaskell_basic_optimized "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_basic_optimized/testhaskell_basic_optimized") set_property (TEST testhaskell_basic_optimized PROPERTY LABELS bindings) set_property (TEST testhaskell_basic_optimized PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib") if (ENABLE_KDB_TESTING) - add_test ( - NAME testhaskell_realworld - COMMAND "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_realworld/testhaskell_realworld" - ) + add_test (testhaskell_realworld "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_realworld/testhaskell_realworld") set_property (TEST testhaskell_realworld PROPERTY LABELS bindings) set_property (TEST testhaskell_realworld APPEND PROPERTY LABELS kdbtests) set_property (TEST testhaskell_realworld PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib") - add_test ( - NAME testhaskell_realworld_optimized - COMMAND "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_realworld_optimized/testhaskell_realworld_optimized" - ) + add_test (testhaskell_realworld_optimized "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_realworld_optimized/testhaskell_realworld_optimized") set_property (TEST testhaskell_realworld_optimized PROPERTY LABELS bindings) set_property (TEST testhaskell_realworld_optimized APPEND PROPERTY LABELS kdbtests) set_property (TEST testhaskell_realworld_optimized PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib") endif (ENABLE_KDB_TESTING) endif (BUILD_TESTING) else (NOT BUILD_STATIC) - exclude_binding (haskell "BUILD_STATIC is currently not compatible with the haskell bindings") + remove_binding (haskell "BUILD_STATIC is currently not compatible with the haskell bindings") endif (NOT BUILD_STATIC) else (HASKELL_FOUND) - exclude_binding (haskell ${HASKELL_NOTFOUND_INFO}) + remove_binding (haskell ${HASKELL_NOTFOUND_INFO}) endif (HASKELL_FOUND) diff --git a/src/bindings/haskell/README.md b/src/bindings/haskell/README.md index 4fdd52c96a5..4e0483bff67 100644 --- a/src/bindings/haskell/README.md +++ b/src/bindings/haskell/README.md @@ -39,16 +39,10 @@ Before installing the remaining dependencies, make sure to update your cabal dat which contains the current list of haskell packages by invoking `cabal update`, this is not done automatically. -- hspec `cabal install hspec` (only if the tests are being built, v2.4.4 tested) -- QuickCheck `cabal install QuickCheck` (only if the tests are being built, v2.10.1 tested) - c2hs > 0.28.2 (older versions may work but are untested) c2hs is available in some package managers and should be installed from there in that case. If c2hs is not available in the package manager (e.g. homebrew on macOS does not include it), - it can be installed via cabal by invoking `cabal install c2hs`. - -To configure the remaining dependencies with a single command, after having installed GHC -and cabal, invoke `cabal update && cabal install hspec QuickCheck c2hs` if your package -manager does not include c2hs, or `cabal update && cabal install hspec QuickCheck` otherwise. + it can be installed via cabal by invoking `cabal update && cabal install c2hs`. ## Limitations diff --git a/src/bindings/haskell/Setup.hs b/src/bindings/haskell/Setup.hs deleted file mode 100644 index 9a994af677b..00000000000 --- a/src/bindings/haskell/Setup.hs +++ /dev/null @@ -1,2 +0,0 @@ -import Distribution.Simple -main = defaultMain diff --git a/src/bindings/haskell/libelektra-haskell.cabal.in b/src/bindings/haskell/libelektra-haskell.cabal.in index 493bc551d5a..d84f5942574 100644 --- a/src/bindings/haskell/libelektra-haskell.cabal.in +++ b/src/bindings/haskell/libelektra-haskell.cabal.in @@ -1,6 +1,6 @@ name: libelektra-haskell version: @KDB_VERSION@ --- synopsis: +synopsis: Haskell bindings for libelektra description: Haskell bindings for libelektra homepage: https://www.libelektra.org license: BSD3 @@ -8,9 +8,11 @@ author: e1528532 maintainer: e1528532@libelektra.org copyright: libelektra.org category: binding -build-type: Simple +build-type: Custom cabal-version: >=1.10 +@CABAL_CUSTOM_SETUP@ + library hs-source-dirs: "@CMAKE_CURRENT_SOURCE_DIR@/src" exposed-modules: Elektra.Key diff --git a/src/plugins/haskell/README.md b/src/plugins/haskell/README.md index a3036e69d02..039d0c4ee4a 100644 --- a/src/plugins/haskell/README.md +++ b/src/plugins/haskell/README.md @@ -1,8 +1,8 @@ - infos = Information about the haskell plugin is in keys below - infos/author = e1528532 - infos/licence = BSD -- infos/needs = -- infos/provides = +- infos/needs = +- infos/provides = - infos/recommends = - infos/placements = - infos/status = maintained experimental @@ -17,18 +17,34 @@ A plugin which takes care about initializing the haskell run-time. This plugin on its own provides a minimalistic implementation to test the basic functionality, but acts as a base for developing further haskell plugins. +To be precise, the following files are required and should use the following +naming scheme, with `` being the name of this plugin (`haskell` in this case): + +- CMakeLists.txt which includes and calls the LibAddHaskellPlugin macro +- `.cabal.in` which is the cabal build file for the plugin, ensure that: + - `hs-source-dirs:` is set to `"@CMAKE_CURRENT_SOURCE_DIR@"` + - `build-depends:` includes `libelektra-haskell` for the haskell bindings + - `build-type:` is set to `Custom` + - `@CABAL_CUSTOM_SETUP@` is included somewhere in case the default Setup.hs is + used +- `testmod_.c` which includes the tests for the plugin Use the cmake command add_haskell_plugin which can used by including LibAddHaskellPlugin. This command will take care about the proper linking of your haskell plugin. Furthermore it uses the c wrapper provided by this plugin so this doesn't have to be done again. +By default plugins get built in a cabal sandbox shared with all other Haskell plugins- +and the Haskell bindings to speed up compilation by compiling commonly used dependencies +just once. Dependencies are automatically resolved according to the cabal build +configuration. + ## Dependencies -* Elektra Haskell bindings installed -* ghc, tested with 8.1.2, may work with older versions as well -* ghc-pkg, usually bundled with ghc -* cabal, the haskell build system, usually bundled with ghc +- Elektra's haskell bindings +- ghc, tested with 8.1.2, may work with older versions as well +- ghc-pkg, usually bundled with ghc +- cabal, the haskell build system, usually bundled with ghc ## Limitations -None. +Currently the Haskell plugin support only executes tests written in C and not directly in Haskell. diff --git a/src/plugins/haskell/Setup.hs b/src/plugins/haskell/Setup.hs new file mode 100644 index 00000000000..81c74767ed9 --- /dev/null +++ b/src/plugins/haskell/Setup.hs @@ -0,0 +1,139 @@ +-- +-- Collects information about the libraries used to build this plugin +-- so they can be statically linked afterwards in order to provide +-- easy-to-package haskell plugins for elektra +-- +{-# LANGUAGE CPP #-} + +module Main (main) where + +import Distribution.Simple +import Distribution.InstalledPackageInfo (InstalledPackageInfo) +import Control.Monad (forM_, when) +import Control.Applicative (liftA2) +import Data.Foldable (toList) +import Data.Maybe (fromJust, listToMaybe, mapMaybe) +import Data.Set (Set) +import Data.List (isSuffixOf, isInfixOf) +import System.IO (hPutStrLn, stderr) +import System.Exit (exitFailure) +import System.Process (readProcess) +import System.Directory (createDirectoryIfMissing) + +import qualified Data.Set as S +import qualified Distribution.Simple.Compiler as C +import qualified Distribution.Simple.Setup as C +import qualified Distribution.Types.LocalBuildInfo as C +import qualified Distribution.Types.PackageDescription as C +import qualified Distribution.Package as C +import qualified Distribution.Simple.LocalBuildInfo as C +import qualified Distribution.Simple.PackageIndex as C +import qualified Distribution.Text as C +import qualified Distribution.Simple.Program as C +import qualified Distribution.Verbosity as V +import qualified Distribution.InstalledPackageInfo as IPI + +#if MIN_VERSION_Cabal(1,24,0) +type PackageIndex a = C.PackageIndex IPI.InstalledPackageInfo +#elif MIN_VERSION_Cabal(1,22,0) +type PackageIndex a = C.PackageIndex (IPI.InstalledPackageInfo_ a) +#else +type PackageIndex a = C.PackageIndex +#endif +data Build = Static | Dynamic + +main :: IO () +main = defaultMainWithHooks (simpleUserHooks { hookedPrograms = [C.simpleProgram "libtool", C.simpleProgram "ln"], + postBuild = elektraHaskellPluginPostBuildHook}) + +elektraHaskellPluginPostBuildHook :: Args -> C.BuildFlags -> C.PackageDescription -> C.LocalBuildInfo -> IO () +elektraHaskellPluginPostBuildHook _ flags pd lbi = do + logV "Preparing the Haskell dependencies.." + isVerbose $ forM_ libraries putStrLn + let ln = C.lookupProgram (C.simpleProgram "ln") $ C.withPrograms lbi + let maybeMainLink = fmap mainLink ln + let maybeMainDepLink = fmap mainDepLink ln + let maybeDepLink = fmap depLink ln + let maybeDepLinks = fmap (`fmap` dependencies) maybeDepLink + let maybeLink = liftA2 (:) (Just $ createDirectoryIfMissing True "haskell") + $ liftA2 (:) maybeMainDepLink + $ liftA2 (:) maybeMainLink maybeDepLinks + maybe linkFailure (\l -> sequence_ l >> logV "Finished generating the post-build output..") maybeLink + -- let maybeLibtool = fmap packWithLibtool $ C.lookupProgram (C.simpleProgram "libtool") $ C.withPrograms lbi + -- let maybeAr = fmap packWithAr $ C.lookupProgram (C.simpleProgram "ar") $ C.withPrograms lbi + -- maybe packFailure (>> logV "Finished packing all the haskell dependencies..") (maybeLibtool <|> maybeAr) + where + verbosity = C.fromFlagOrDefault V.normal $ C.buildVerbosity flags + isVerbose = when (verbosity >= V.verbose) + logV = isVerbose . putStrLn + failure str = hPutStrLn stderr str >> exitFailure + packFailure = failure "Failed to locate libtool and ar, no program to pack the dependencies" + linkFailure = failure "Failed to locate ln" + libraryName = "lib" ++ C.getHSLibraryName (C.localUnitId lbi) ++ suffix lbi + libraryPath = C.buildDir lbi ++ "/" ++ libraryName + libraries = (libraryPath :) $ map (showLibrary lbi) dependencies + dependencies = filter (not . isGlobalLibrary) $ getDependencyInstalledPackageInfos lbi + createMRIScript sl = ("create " ++ libraryName) : fmap ("addlib " ++) sl ++ ["save", "end"] + packWithAr ar = logV "packing with ar" >> + readProcess (C.programPath ar) ["-M"] (unlines $ createMRIScript libraries) >>= logV + packWithLibtool lt = logV "packing with libtool" >> + readProcess (C.programPath lt) ("-static" : "-o" : libraryName : libraries) "" >>= logV + mainLink ln = let filename = "libHS" ++ (C.unPackageName . C.packageName . C.package) pd ++ suffix lbi in + logV "linking main library" >> + readProcess (C.programPath ln) ["-f", libraryPath, filename] "" >>= logV + mainDepLink ln = logV "linking main library for libraries" >> + readProcess (C.programPath ln) ["-f", libraryPath, "haskell/" ++ libraryName] "" >>= logV + depLink ln lib = logV ("linking library " ++ showLibrary lbi lib) >> + readProcess (C.programPath ln) ["-f", showLibrary lbi lib, "haskell/libHS" ++ getLibraryName lib ++ suffix lbi] "" + >>= logV + +-- The globally installed ghc libs which we don't want to link statically +-- We only want to link external dependencies grabbed via hackage, not those +-- that will be present on target systems due to the ghc requirement. +-- Later on we can include a flag to optionally link everything statically +-- thus target platforms wouldn't require ghc to be installed. +ghcLibs :: [String] +ghcLibs = ["base", "ghc-prim", "integer-gmp", "rts"] + +isGlobalLibrary :: InstalledPackageInfo -> Bool +isGlobalLibrary = liftA2 (||) blacklisted global + where + blacklisted = flip any ghcLibs . (==) . C.display . C.pkgName . IPI.sourcePackageId + global = not . any (".cabal-sandbox" `isInfixOf`) . IPI.libraryDirs + +findTransitiveDependencies :: PackageIndex a -> Set C.UnitId -> Set C.UnitId +findTransitiveDependencies pkgIdx initial = go S.empty (S.toList initial) + where + go set [] = set + go set (q : qs) + | q `S.member` set = go set qs + | otherwise = maybe (go set qs) (go (S.insert q set) . (++ qs) . IPI.depends) + $ C.lookupUnitId pkgIdx q + +getDependencyInstalledPackageIds :: C.LocalBuildInfo -> Set C.UnitId +getDependencyInstalledPackageIds lbi = findTransitiveDependencies (C.installedPkgs lbi) $ + S.fromList [ ipId | componentLbi <- toList (C.componentGraph lbi) + , (ipId, _) <- C.componentPackageDeps componentLbi] + +getDependencyInstalledPackageInfos :: C.LocalBuildInfo -> [InstalledPackageInfo] +getDependencyInstalledPackageInfos lbi = mapMaybe (C.lookupUnitId $ C.installedPkgs lbi) + $ S.toList (getDependencyInstalledPackageIds lbi) + +showLibrary :: C.LocalBuildInfo -> InstalledPackageInfo -> String +showLibrary lbi ipi = fromJust (getLibraryDir lbi ipi) ++ "/libHS" ++ getLibraryName ipi ++ suffix lbi + +getLibraryDir :: C.LocalBuildInfo -> InstalledPackageInfo -> Maybe String +getLibraryDir lbi ipi = listToMaybe $ case buildType lbi of + Static -> filter (getLibraryName ipi `isSuffixOf`) $ IPI.libraryDirs ipi + Dynamic -> IPI.libraryDynDirs ipi + +getLibraryName :: InstalledPackageInfo -> String +getLibraryName = C.display . IPI.installedUnitId + +buildType :: C.LocalBuildInfo -> Build +buildType lbi = if C.withSharedLib lbi then Dynamic else Static + +suffix :: C.LocalBuildInfo -> String +suffix lbi = case buildType lbi of + Static -> ".a" + Dynamic -> '-' : filter (/= '-') (C.showCompilerId $ C.compiler lbi) ++ ".dylib" diff --git a/src/plugins/haskell/haskell.cabal.in b/src/plugins/haskell/haskell.cabal.in index b02920902bc..41eeee80143 100644 --- a/src/plugins/haskell/haskell.cabal.in +++ b/src/plugins/haskell/haskell.cabal.in @@ -1,6 +1,6 @@ name: haskell version: 0.1.0.0 --- synopsis: +synopsis: Base for developing haskell plugins description: Base for developing haskell plugins homepage: https://www.libelektra.org license: BSD3 @@ -8,9 +8,11 @@ author: e1528532 maintainer: e1528532@libelektra.org copyright: libelektra.org category: plugin -build-type: Simple +build-type: Custom cabal-version: >=1.10 +@CABAL_CUSTOM_SETUP@ + library hs-source-dirs: "@CMAKE_CURRENT_SOURCE_DIR@" exposed-modules: Elektra.Haskell @@ -18,7 +20,7 @@ library , libelektra-haskell default-language: Haskell2010 other-extensions: ForeignFunctionInterface - ghc-options: -fPIC + ghc-options: source-repository head type: git diff --git a/src/plugins/haskell/haskell.h.in b/src/plugins/haskell/haskell.h.in index b5f1bd24f96..d3864542e0f 100644 --- a/src/plugins/haskell/haskell.h.in +++ b/src/plugins/haskell/haskell.h.in @@ -3,7 +3,7 @@ * * @brief Header for haskell plugin * - * @copyrigh*t BSD License (see LICENSE.md or https://www.libelektra.org) + * @copyright BSD License (see LICENSE.md or https://www.libelektra.org) * */ From ff60bb7bda60c7efb996603de1e691ffa3f429c3 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Thu, 28 Dec 2017 20:19:28 +0100 Subject: [PATCH 16/36] haskell-cabal-sandboxing: unify and fix indendation in haskell files, shorten some lines --- src/bindings/haskell/examples/example_kdb.hs | 23 ++-- src/bindings/haskell/examples/example_key.hs | 73 ++++++------ .../haskell/examples/example_keyset.hs | 72 ++++++------ src/bindings/haskell/src/Elektra/Ease.chs | 7 +- src/bindings/haskell/src/Elektra/KDB.chs | 8 +- src/bindings/haskell/src/Elektra/Key.chs | 29 +++-- src/bindings/haskell/src/Elektra/KeySet.chs | 25 +++-- src/bindings/haskell/src/Elektra/Plugin.chs | 35 +++--- src/bindings/haskell/test/Elektra.hs | 104 ++++++++++-------- src/bindings/haskell/test/ElektraRealWorld.hs | 34 +++--- 10 files changed, 228 insertions(+), 182 deletions(-) diff --git a/src/bindings/haskell/examples/example_kdb.hs b/src/bindings/haskell/examples/example_kdb.hs index 4ca6be50fda..f176901e3ad 100644 --- a/src/bindings/haskell/examples/example_kdb.hs +++ b/src/bindings/haskell/examples/example_kdb.hs @@ -1,4 +1,4 @@ -module Example where +module Example (main) where import Elektra.Key import Elektra.KeySet @@ -6,12 +6,15 @@ import Elektra.KDB main :: IO () main = do - parent <- keyNew "user/MyApp" - putStrLn "Opening the database." - kdbOpen parent $ \kdb -> do - ks <- ksNew 100 - kdbGet kdb ks parent - key <- ifKey (ksLookupByName ks "user/MyApp/mykey") return (keyNew "user/MyApp/mykey" >>= \n -> ksAppendKey ks n >> return n) - keySet key "new_value" - kdbSet kdb ks parent - putStrLn "The database gets closed automatically after the lambda has been executed." + parent <- keyNew "user/MyApp" + putStrLn "Opening the database." + ret <- kdbOpen parent $ \kdb -> do + ks <- ksNew 100 + _ <- kdbGet kdb ks parent + key <- ifKey (ksLookupByName ks "user/MyApp/mykey") + return + (keyNew "user/MyApp/mykey" >>= \n -> ksAppendKey ks n >> return n) + _ <- keySet key "new_value" + kdbSet kdb ks parent + putStrLn $ "kdbOpen returned " ++ show ret + putStrLn "The database gets closed automatically after the lambda has been executed." diff --git a/src/bindings/haskell/examples/example_key.hs b/src/bindings/haskell/examples/example_key.hs index 564e865a9ae..054e422a361 100644 --- a/src/bindings/haskell/examples/example_key.hs +++ b/src/bindings/haskell/examples/example_key.hs @@ -1,44 +1,45 @@ -module Example where +module Example (main) where import Elektra.Key -import Control.Monad import Text.Printf +putEmptyLn :: IO () putEmptyLn = putStrLn "" main :: IO () main = do - key1 <- keyNewWithValue "user/key1" "some_value" - printf "Key1 %s" (show key1) - putStrLn "Every Key has properties. Some are read only, some are read+write." - putStrLn "Properties of Key1:" - keyName key1 >>= printf " key1.name = %s\n" - keyString key1 >>= printf " key1.value = %s\n" - keyGetBaseName key1 >>= printf " key1.basename = %s\n" - keyGetFullName key1 >>= printf " key1.fullname = %s\n" - putEmptyLn - - keySet key1 5 -- allows to set it to anything that is an instance of Show - keyString key1 >>= printf "We changed the value of Key1. New value is %s.\n" - putEmptyLn - - key2 <- keyDup key1 - printf "Key2 is a copy of Key1. Do they match? %s.\n" (show $ key1 == key2) - putEmptyLn - - keySetName key1 "system/key1" - keyName key1 >>= printf "We changed name of Key1. New name is %s.\n" - printf "Do they still match? %s\n" (show $ key1 == key2) - putEmptyLn - - keySetBaseName key1 "key1_changed" - keyName key1 >>= printf "Changing the basename only is possible as well. New name is %s.\n" - putEmptyLn - - keySetMeta key1 "foo" "bar" - keySetMeta key1 "owner" "e1528532" - keySetMeta key1 "comment" "this is my example key" - putStrLn "Keys can have metadata. We can iterate over or fetch them by name." - putStrLn "Meta data of Key1 with their values:" - keyListMeta key1 >>= mapM_ print - putStrLn "Remember: Metadata is returned as a Key object." + key1 <- keyNewWithValue "user/key1" "some_value" + printf "Key1 %s" (show key1) + putStrLn "Every Key has properties. Some are read only, some are read+write." + putStrLn "Properties of Key1:" + keyName key1 >>= printf " key1.name = %s\n" + keyString key1 >>= printf " key1.value = %s\n" + keyGetBaseName key1 >>= printf " key1.basename = %s\n" + keyGetFullName key1 >>= printf " key1.fullname = %s\n" + putEmptyLn + + ret <- keySet key1 (5 :: Int) -- allows to set it to anything that is an instance of Show + putStrLn $ "keySet returned " ++ show ret + keyString key1 >>= printf "We changed the value of Key1. New value is %s.\n" + putEmptyLn + + key2 <- keyDup key1 + printf "Key2 is a copy of Key1. Do they match? %s.\n" (show $ key1 == key2) + putEmptyLn + + _ <- keySetName key1 "system/key1" + keyName key1 >>= printf "We changed name of Key1. New name is %s.\n" + printf "Do they still match? %s\n" (show $ key1 == key2) + putEmptyLn + + _ <- keySetBaseName key1 "key1_changed" + keyName key1 >>= printf "Changing the basename only is possible as well. New name is %s.\n" + putEmptyLn + + _ <- keySetMeta key1 "foo" "bar" + _ <- keySetMeta key1 "owner" "e1528532" + _ <- keySetMeta key1 "comment" "this is my example key" + putStrLn "Keys can have metadata. We can iterate over or fetch them by name." + putStrLn "Meta data of Key1 with their values:" + keyListMeta key1 >>= mapM_ print + putStrLn "Remember: Metadata is returned as a Key object." diff --git a/src/bindings/haskell/examples/example_keyset.hs b/src/bindings/haskell/examples/example_keyset.hs index ab99ea17470..f313d28cf80 100644 --- a/src/bindings/haskell/examples/example_keyset.hs +++ b/src/bindings/haskell/examples/example_keyset.hs @@ -1,45 +1,47 @@ -module Example where +module Example (main) where import Elektra.Key import Elektra.KeySet import Control.Monad import Text.Printf +putEmptyLn :: IO () putEmptyLn = putStrLn "" main :: IO () main = do - ks1 <- ksNew 100 - key1 <- keyNew "user/key1" - ksAppendKey ks1 key1 - keyNew "user/key2" >>= ksAppendKey ks1 - keyNew "user/key3" >>= ksAppendKey ks1 - - ksGetSize ks1 >>= printf "KeySet1 has %d keys\n" - putEmptyLn - - putStrLn "We can easily iterate over the keyset to check out its content:" - ksList ks1 >>= mapM_ print -- or `print ks1` as KeySet is an instance of Show - putStrLn "This works the other direction too:" - ksList ks1 >>= mapM_ print . reverse - putEmptyLn - - putStrLn "We can check if a key is in a keyset:" - ksLookupByName ks1 "user/key1" >>= keyPtrNull >>= printf " Is user/key1 in KeySet1? %s\n" . show . not - putStrLn "This works with Key objects too:" - ksLookup ks1 key1 >>= keyPtrNull >>= printf " Is Key(user/key1) in KeySet1? %s\n" . show . not - putEmptyLn - - putStrLn "We can create shallow copies and remove keys without affecting other keysets:" - ks2 <- ksDup ks1 - ksPop ks2 - liftM2 (printf " KeySet2 now has %d keys while KeySet1 still has %d keys") (ksGetSize ks2) (ksGetSize ks1) >>= putStrLn - putEmptyLn - - putStrLn "In Haskell we can easily create deep copies and modify the keys inside:" - ksHead ks1 >>= (flip . flip keySetMeta) "foo" "bar" - ks3 <- ksGetSize ks1 >>= ksNew - ksList ks1 >>= mapM_ (>>= ksAppendKey ks3) . fmap keyDup - ksHead ks3 >>= (flip . flip keySetMeta) "foo" "changed" - ksHead ks1 >>= flip keyGetMeta "foo" >>= keyString >>= printf " KeySet1 has metadata %s\n" - ksHead ks3 >>= flip keyGetMeta "foo" >>= keyString >>= printf " KeySet3 has metadata %s\n" + ks1 <- ksNew 100 + key1 <- keyNew "user/key1" + _ <- ksAppendKey ks1 key1 + _ <- keyNew "user/key2" >>= ksAppendKey ks1 + _ <- keyNew "user/key3" >>= ksAppendKey ks1 + + ksGetSize ks1 >>= printf "KeySet1 has %d keys\n" + putEmptyLn + + putStrLn "We can easily iterate over the keyset to check out its content:" + ksList ks1 >>= mapM_ print -- or `print ks1` as KeySet is an instance of Show + putStrLn "This works the other direction too:" + ksList ks1 >>= mapM_ print . reverse + putEmptyLn + + putStrLn "We can check if a key is in a keyset:" + ksLookupByName ks1 "user/key1" >>= keyPtrNull >>= printf " Is user/key1 in KeySet1? %s\n" . show . not + putStrLn "This works with Key objects too:" + ksLookup ks1 key1 >>= keyPtrNull >>= printf " Is Key(user/key1) in KeySet1? %s\n" . show . not + putEmptyLn + + putStrLn "We can create shallow copies and remove keys without affecting other keysets:" + ks2 <- ksDup ks1 + _ <- ksPop ks2 + liftM2 (printf " KeySet2 now has %d keys while KeySet1 still has %d keys") (ksGetSize ks2) (ksGetSize ks1) + >>= putStrLn + putEmptyLn + + putStrLn "In Haskell we can easily create deep copies and modify the keys inside:" + _ <- ksHead ks1 >>= (flip . flip keySetMeta) "foo" "bar" + ks3 <- ksGetSize ks1 >>= ksNew + ksList ks1 >>= mapM_ (>>= ksAppendKey ks3) . fmap keyDup + _ <- ksHead ks3 >>= (flip . flip keySetMeta) "foo" "changed" + ksHead ks1 >>= flip keyGetMeta "foo" >>= keyString >>= printf " KeySet1 has metadata %s\n" + ksHead ks3 >>= flip keyGetMeta "foo" >>= keyString >>= printf " KeySet3 has metadata %s\n" diff --git a/src/bindings/haskell/src/Elektra/Ease.chs b/src/bindings/haskell/src/Elektra/Ease.chs index eb8e14fd427..6f66a342342 100644 --- a/src/bindings/haskell/src/Elektra/Ease.chs +++ b/src/bindings/haskell/src/Elektra/Ease.chs @@ -1,4 +1,8 @@ -module Elektra.Ease (ArrayValidateNameResult (..), keyGetRelativeName, arrayValidateName, arrayIncName, arrayGet, arrayGetNextKey) where +module Elektra.Ease ( + ArrayValidateNameResult (..), + arrayValidateName, arrayIncName, arrayGetNextKey, arrayGet, + keyGetRelativeName + ) where #include @@ -11,6 +15,7 @@ module Elektra.Ease (ArrayValidateNameResult (..), keyGetRelativeName, arrayVali data ArrayValidateNameResult = Invalid | Start | Element deriving (Show, Eq, Enum) +arrayValidateName :: Key -> IO (ArrayValidateNameResult) arrayValidateName = fmap parseResult . elektraArrayValidateName where parseResult 0 = Start diff --git a/src/bindings/haskell/src/Elektra/KDB.chs b/src/bindings/haskell/src/Elektra/KDB.chs index 056ea48a251..5de2a0278fe 100644 --- a/src/bindings/haskell/src/Elektra/KDB.chs +++ b/src/bindings/haskell/src/Elektra/KDB.chs @@ -13,10 +13,10 @@ module Elektra.KDB (kdbOpen, kdbGet, kdbSet) where kdbOpen :: Key -> (KDB -> IO a) -> IO a kdbOpen parentKey actions = do - kdb <- kdbOpenRaw parentKey - res <- actions kdb - kdbClose kdb parentKey - return res + kdb <- kdbOpenRaw parentKey + res <- actions kdb + kdbClose kdb parentKey + return res {#fun unsafe kdbOpen as kdbOpenRaw {`Key'} -> `KDB' #} {#fun unsafe kdbClose {`KDB', `Key'} -> `Int' #} {#fun unsafe kdbGet {`KDB', `KeySet', `Key'} -> `Int' #} diff --git a/src/bindings/haskell/src/Elektra/Key.chs b/src/bindings/haskell/src/Elektra/Key.chs index 3e6fb60ece3..e155f83ede3 100644 --- a/src/bindings/haskell/src/Elektra/Key.chs +++ b/src/bindings/haskell/src/Elektra/Key.chs @@ -1,10 +1,21 @@ -module Elektra.Key (Key (..), Namespace (..), withKey, ElektraKeyVarargs (KeyMetaName, KeyBinary, KeyComment, KeyOwner), - keyNew, keyNewWithValue, keyNewWithFlagsAndValue, keyDup, keyCopy, keyClear, keyIncRef, keyDecRef, keyGetRef, - keyName, keyGetNameSize, keyUnescapedName, keyGetUnescapedNameSize, keySetName, keyGetFullNameSize, keyGetFullName, - keyAddName, keyBaseName, keyGetBaseName, keyGetBaseNameSize, keyAddBaseName, keySetBaseName, keyDeleteBaseName, keyGetNamespace, - keyString, keyGetValueSize, keySetString, keySet, - keyRewindMeta, keyNextMeta, keyCurrentMeta, keyCopyMeta, keyCopyAllMeta, keyGetMeta, keySetMeta, keyListMeta, - keyCmp, keyNeedSync, keyIsBelow, keyIsDirectBelow, keyRel, keyIsInactive, keyIsBinary, keyIsString, keyPtrNull, ifKey) where +module Elektra.Key ( + Key (..), Namespace (..), ElektraKeyVarargs (KeyMetaName, KeyBinary, KeyComment, KeyOwner), + keyNew, keyNewWithValue, keyNewWithFlagsAndValue, + keyDup, keyCopy, keyClear, + keyIncRef, keyDecRef, keyGetRef, + keyName, keyGetNameSize, keySetName, keyAddName, + keyUnescapedName, keyGetUnescapedNameSize, + keyGetFullNameSize, keyGetFullName, + keyBaseName, keyGetBaseName, keyGetBaseNameSize, keyAddBaseName, keySetBaseName, keyDeleteBaseName, + keyGetNamespace, + keyString, keyGetValueSize, keySetString, keySet, + keyRewindMeta, keyNextMeta, keyCurrentMeta, + keyCopyMeta, keyCopyAllMeta, keyGetMeta, keySetMeta, keyListMeta, + keyCmp, keyNeedSync, + keyIsBelow, keyIsDirectBelow, + keyRel, keyIsInactive, keyIsBinary, keyIsString, keyPtrNull, + ifKey, withKey + ) where #include import Foreign.Marshal.Alloc (allocaBytes) @@ -63,7 +74,9 @@ keyNewWithFlagsAndValue name flags value = do {#fun unsafe variadic keyNew[keyswitch_t, const char *, keyswitch_t] as keyNewRawWithValue {`String', `ElektraKeyVarargs', `String', `ElektraKeyVarargs'} -> `Key' #} {#fun unsafe variadic keyNew[keyswitch_t, keyswitch_t, keyswitch_t, const char *, keyswitch_t] - as keyNewRawWithFlagsAndValue {`String', `ElektraKeyVarargs', `ElektraKeyVarargs', `ElektraKeyVarargs', `String', `ElektraKeyVarargs'} -> `Key' #} + as keyNewRawWithFlagsAndValue + {`String', `ElektraKeyVarargs', `ElektraKeyVarargs', `ElektraKeyVarargs', `String', `ElektraKeyVarargs'} + -> `Key' #} keyDup :: Key -> IO Key keyDup key = do dup <- keyDupRaw key diff --git a/src/bindings/haskell/src/Elektra/KeySet.chs b/src/bindings/haskell/src/Elektra/KeySet.chs index 523cee9e2e3..4d9151f650d 100644 --- a/src/bindings/haskell/src/Elektra/KeySet.chs +++ b/src/bindings/haskell/src/Elektra/KeySet.chs @@ -1,6 +1,13 @@ -module Elektra.KeySet (KeySet (..), LookupOptions (..), withKeySet, ElektraCursor, ksNew, ksDup, ksCopy, ksDel, ksGetSize, ksNeedSync, - ksAppendKey, ksAppend, ksCut, ksPop, ksRewind, ksNext, ksCurrent, ksHead, ksTail, ksList, - ksGetCursor, ksSetCursor, ksAtCursor, ksLookup, ksLookupO, ksLookupByName, ksLookupByNameO) where +module Elektra.KeySet ( + KeySet (..), LookupOptions (..), ElektraCursor, + withKeySet, + ksNew, ksDup, ksCopy, ksDel, + ksGetSize, ksNeedSync, + ksAppendKey, ksAppend, ksCut, ksPop, + ksRewind, ksNext, ksCurrent, ksHead, ksTail, ksList, + ksGetCursor, ksSetCursor, ksAtCursor, + ksLookup, ksLookupO, ksLookupByName, ksLookupByNameO + ) where {#import Elektra.Key#} import Control.Monad (liftM, liftM2) @@ -69,12 +76,12 @@ ksLookupByName ks k = ksLookupByNameO ks k KdbONone {#fun unsafe ksLookupByName as ksLookupByNameO {`KeySet', `String', `LookupOptions'} -> `Key' #} ksList :: KeySet -> IO [Key] ksList ks = ksRewind ks >> list [] - where - list res = do - cur <- ksNext ks - isNull <- keyPtrNull cur - if isNull then return res else liftM (cur :) (list res) - + where + list res = do + cur <- ksNext ks + isNull <- keyPtrNull cur + if isNull then return res else liftM (cur :) (list res) + -- *** -- COMMON HASKELL TYPE CLASSES -- unsafePerformIO should be ok here, as we use ksDup to "hide" the side effect of ksRewind diff --git a/src/bindings/haskell/src/Elektra/Plugin.chs b/src/bindings/haskell/src/Elektra/Plugin.chs index 03adf6aef8b..7b87f2f7096 100644 --- a/src/bindings/haskell/src/Elektra/Plugin.chs +++ b/src/bindings/haskell/src/Elektra/Plugin.chs @@ -1,7 +1,11 @@ -module Elektra.Plugin (Plugin, elektraPluginGetConfig, elektraPluginSetData, elektraPluginGetData, - elektraPluginOpenWith, elektraPluginCloseWith, elektraPluginGetWith, elektraPluginSetWith, - elektraPluginErrorWith, elektraPluginCheckConfigWith, - PluginStatus (..)) where +module Elektra.Plugin ( + Plugin, PluginStatus (..), + elektraPluginGetConfig, + elektraPluginSetData, elektraPluginGetData, + elektraPluginOpenWith, elektraPluginCloseWith, + elektraPluginGetWith, elektraPluginSetWith, + elektraPluginErrorWith, elektraPluginCheckConfigWith + ) where {#import Elektra.Key#} {#import Elektra.KeySet#} @@ -19,14 +23,14 @@ import Control.Monad (join, liftM, liftM2, liftM3) data PluginStatus = Error | NoUpdate | Success deriving (Show, Eq) instance Enum PluginStatus where - fromEnum Error = -1 - fromEnum NoUpdate = 0 - fromEnum Success = 1 + fromEnum Error = -1 + fromEnum NoUpdate = 0 + fromEnum Success = 1 - toEnum (-1) = Error - toEnum 0 = NoUpdate - toEnum 1 = Success - toEnum unmatched = error ("PluginStatus.toEnum: Cannot match " ++ show unmatched) + toEnum (-1) = Error + toEnum 0 = NoUpdate + toEnum 1 = Success + toEnum unmatched = error ("PluginStatus.toEnum: Cannot match " ++ show unmatched) -- *** -- PLUGIN METHODS @@ -57,12 +61,15 @@ elektraPluginErrorWith :: (Plugin -> KeySet -> Key -> IO PluginStatus) -> Ptr Pl elektraPluginErrorWith = elektraPlugin3 elektraPluginCheckConfigWith :: (Key -> KeySet -> IO PluginStatus) -> Ptr Key -> Ptr KeySet -> IO Int -elektraPluginCheckConfigWith f k ks = liftM fromEnum $ join $ liftM2 f (liftM Key $ newForeignPtr_ k) (liftM KeySet $ newForeignPtr_ ks) +elektraPluginCheckConfigWith f k ks = liftM fromEnum $ join $ + liftM2 f (liftM Key $ newForeignPtr_ k) (liftM KeySet $ newForeignPtr_ ks) -- shared parameter conversation elektraPlugin2 :: (Plugin -> Key -> IO PluginStatus) -> Ptr Plugin -> Ptr Key -> IO Int -elektraPlugin2 f p k = liftM fromEnum $ join $ liftM2 f (liftM Plugin $ newForeignPtr_ p) (liftM Key $ newForeignPtr_ k) +elektraPlugin2 f p k = liftM fromEnum $ join $ + liftM2 f (liftM Plugin $ newForeignPtr_ p) (liftM Key $ newForeignPtr_ k) elektraPlugin3 :: (Plugin -> KeySet -> Key -> IO PluginStatus) -> Ptr Plugin -> Ptr KeySet -> Ptr Key -> IO Int -elektraPlugin3 f p ks k = liftM fromEnum $ join $ liftM3 f (liftM Plugin $ newForeignPtr_ p) (liftM KeySet $ newForeignPtr_ ks) (liftM Key $ newForeignPtr_ k) +elektraPlugin3 f p ks k = liftM fromEnum $ join $ + liftM3 f (liftM Plugin $ newForeignPtr_ p) (liftM KeySet $ newForeignPtr_ ks) (liftM Key $ newForeignPtr_ k) diff --git a/src/bindings/haskell/test/Elektra.hs b/src/bindings/haskell/test/Elektra.hs index fd27a614b00..7e7064c841a 100644 --- a/src/bindings/haskell/test/Elektra.hs +++ b/src/bindings/haskell/test/Elektra.hs @@ -1,62 +1,70 @@ -module Main where +module Main (main) where import Elektra.Key import Elektra.KeySet -import Elektra.KDB import Test.Hspec import Test.QuickCheck -import Control.Monad +import Control.Monad (join, liftM2) main :: IO () main = hspec $ do - describe "Key" $ do - it "does what i want" $ testSingleKeyOp name keyGetNameSize 25 - it "returns the correct name size" $ testSingleKeyOp name keyGetNameSize 25 - it "returns the correct full name size" $ testSingleKeyOp name keyGetFullNameSize 25 - it "returns the correct full name" $ testSingleKeyOp name keyGetFullName name - it "returns the correct base name size" $ testSingleKeyOp name keyGetBaseNameSize 18 - it "returns the correct base name" $ testSingleKeyOp name keyBaseName "testhaskell_cabal" - it "returns the correct unescaped name size" $ testSingleKeyOp name keyGetUnescapedNameSize 25 - it "returns the correct unescaped name" $ testSingleKeyOp name keyUnescapedName "\0tests\0testhaskell_cabal\0" - it "returns the correct namespace" $ testSingleKeyOp name keyGetNamespace KeyNsCascading - it "creates a new key successfully with the correct name" $ testSingleKeyOp name keyName name - it "sets the keys name" $ testKeyModOp name (`keySetName` otherName) keyName otherName - it "sets the base name" $ testKeyModOp (name ++ "/baseName") (`keySetBaseName` other) keyName (name ++ "/" ++ other) - it "adds an escaped name" $ testKeyModOp name (`keyAddName` otherName) keyName (name ++ otherName) - it "adds a base name" $ testKeyModOp name (`keyAddBaseName` "haskell") keyName (name ++ "/haskell") - it "supports multiple operations on the same key" $ do - key <- keyNew name - keyAddBaseName key "haskell" - keyName key >>= (`shouldBe` name ++ "/haskell") - keyAddBaseName key "other" - keyName key >>= (`shouldBe` name ++ "/haskell/other") - it "creates arbitrary key names" $ property $ forAll genSafeString $ \s1 -> forAll genSafeString $ \s2 -> do - key <- keyNew ('/' : s1) - keyAddBaseName key s2 - keyBaseName key >>= (`shouldBe` s2) - keyName key >>= (`shouldBe` accepted s1 s2) - describe "KeySet" $ do - it "creates a new empty keyset" $ testSingleKeySetOp 5 ksGetSize 0 - it "creates a duplicate keyset which is not the same" $ do - key <- keyNew name - ks <- ksNew 1 - ksAppendKey ks key - ks2 <- ksDup ks - join $ liftM2 shouldBe (ksGetSize ks) (ksGetSize ks2) - ksPop ks - join $ liftM2 shouldNotBe (ksGetSize ks) (ksGetSize ks2) - where - name = "/tests/testhaskell_cabal" - otherName = "/tests/testhaskell_cabal/other" - other = "other" - accepted s1 s2 - | null s1 && null s2 = "/%" - | null s1 = '/':s2 - | null s2 = '/':s1 ++ "/%" - | otherwise = '/':s1 ++ '/':s2 + describe "Key" $ do + it "does what i want" $ testSingleKeyOp name keyGetNameSize 25 + it "returns the correct name size" $ testSingleKeyOp name keyGetNameSize 25 + it "returns the correct full name size" $ testSingleKeyOp name keyGetFullNameSize 25 + it "returns the correct full name" $ testSingleKeyOp name keyGetFullName name + it "returns the correct base name size" $ testSingleKeyOp name keyGetBaseNameSize 18 + it "returns the correct base name" $ testSingleKeyOp name keyBaseName "testhaskell_cabal" + it "returns the correct unescaped name size" $ testSingleKeyOp name keyGetUnescapedNameSize 25 + it "returns the correct unescaped name" $ testSingleKeyOp name keyUnescapedName "\0tests\0testhaskell_cabal\0" + it "returns the correct namespace" $ testSingleKeyOp name keyGetNamespace KeyNsCascading + it "creates a new key successfully with the correct name" $ testSingleKeyOp name keyName name + it "sets the keys name" $ testKeyModOp name (`keySetName` otherName) keyName otherName + it "sets the base name" $ testKeyModOp (name ++ "/baseName") (`keySetBaseName` other) keyName (name ++ "/" ++ other) + it "adds an escaped name" $ testKeyModOp name (`keyAddName` otherName) keyName (name ++ otherName) + it "adds a base name" $ testKeyModOp name (`keyAddBaseName` "haskell") keyName (name ++ "/haskell") + it "supports multiple operations on the same key" $ do + key <- keyNew name + _ <- keyAddBaseName key "haskell" + keyName key >>= (`shouldBe` name ++ "/haskell") + _ <- keyAddBaseName key "other" + keyName key >>= (`shouldBe` name ++ "/haskell/other") + it "creates arbitrary key names" $ property $ forAll genSafeString $ \s1 -> forAll genSafeString $ \s2 -> do + key <- keyNew ('/' : s1) + _ <- keyAddBaseName key s2 + keyBaseName key >>= (`shouldBe` s2) + keyName key >>= (`shouldBe` accepted s1 s2) + describe "KeySet" $ do + it "creates a new empty keyset" $ testSingleKeySetOp 5 ksGetSize 0 + it "creates a duplicate keyset which is not the same" $ do + key <- keyNew name + ks <- ksNew 1 + _ <- ksAppendKey ks key + ks2 <- ksDup ks + join $ liftM2 shouldBe (ksGetSize ks) (ksGetSize ks2) + _ <- ksPop ks + join $ liftM2 shouldNotBe (ksGetSize ks) (ksGetSize ks2) + where + name = "/tests/testhaskell_cabal" + otherName = "/tests/testhaskell_cabal/other" + other = "other" + accepted s1 s2 + | null s1 && null s2 = "/%" + | null s1 = '/':s2 + | null s2 = '/':s1 ++ "/%" + | otherwise = '/':s1 ++ '/':s2 + +-- some helper functions to abstract common test patterns + +testSingleKeyOp :: (Eq a, Show a) => String -> (Key -> IO a) -> a -> IO () testSingleKeyOp name fn expected = keyNew name >>= fn >>= (`shouldBe` expected) + +testKeyModOp :: (Eq b, Show b) => String -> (Key -> IO a) -> (Key -> IO b) -> b -> IO () testKeyModOp name modFn testFn expected = keyNew name >>= (\x -> modFn x >> testFn x >>= (`shouldBe` expected)) + +testSingleKeySetOp :: (Eq a, Show a) => Int -> (KeySet -> IO a) -> a -> IO () testSingleKeySetOp size fn expected = ksNew size >>= fn >>= (`shouldBe` expected) +genSafeString :: Gen String genSafeString = listOf $ elements ['a'..'z'] diff --git a/src/bindings/haskell/test/ElektraRealWorld.hs b/src/bindings/haskell/test/ElektraRealWorld.hs index 4e1d3b54023..99b8e5f3956 100644 --- a/src/bindings/haskell/test/ElektraRealWorld.hs +++ b/src/bindings/haskell/test/ElektraRealWorld.hs @@ -1,4 +1,4 @@ -module Main where +module Main (main) where import Elektra.Key import Elektra.KeySet @@ -6,19 +6,19 @@ import Elektra.KDB import Test.Hspec main :: IO () -main = hspec $ do - describe "KDB" $ it "creates a new kdb connection, saves a key, closes the connection, reopens it and checks the stored key" $ do - parent <- keyNew "/parent" - ks <- ksNew 1 - kdbOpen parent $ \kdb -> do - kdbGet kdb ks parent - keyNew haskellPersisted >>= ksAppendKey ks - kdbSet kdb ks parent - ksAfter <- ksNew 1 - kdbOpen parent $ \kdbAfter -> do - kdbGet kdbAfter ksAfter parent - test <- ksLookupByName ksAfter haskellPersisted - name <- keyName test - ksLookupByName ksAfter haskellPersisted >>= keyName >>= (`shouldBe` haskellPersisted) - where - haskellPersisted = "user/tests/testhaskell_cabal/haskellPersisted" +main = hspec $ + describe "KDB" + $ it "creates a new kdb connection, saves a key, closes the connection, reopens it and checks the stored key" + $ do + parent <- keyNew "/parent" + ks <- ksNew 1 + _ <- kdbOpen parent $ \kdb -> do + _ <- kdbGet kdb ks parent + _ <- keyNew haskellPersisted >>= ksAppendKey ks + kdbSet kdb ks parent + ksAfter <- ksNew 1 + kdbOpen parent $ \kdbAfter -> do + _ <- kdbGet kdbAfter ksAfter parent + ksLookupByName ksAfter haskellPersisted >>= keyName >>= (`shouldBe` haskellPersisted) + where + haskellPersisted = "user/tests/testhaskell_cabal/haskellPersisted" From ffc8b86c28a7b5507618eaad7c8dab6a5ed5a6b5 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Thu, 28 Dec 2017 23:10:59 +0100 Subject: [PATCH 17/36] haskell-cabal-sandboxing: fix another rpath issue, adjust travis config --- .travis.yml | 5 ++--- cmake/Modules/LibAddHaskellPlugin.cmake | 18 ++++++++++++++++-- src/bindings/haskell/CMakeLists.txt | 10 +++------- src/bindings/haskell/src/Elektra/Ease.chs | 4 ++-- src/plugins/haskell/haskell.c.in | 4 ++-- 5 files changed, 25 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index a84c865674a..71d15e0d0ce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,7 @@ cache: $HOME/.m2 $HOME/.cabal $HOME/Library/Haskell - $TRAVIS_BUILD_DIR/../build/src/bindings/haskell/.cabal-sandbox - $TRAVIS_BUILD_DIR/../build/src/plugins/haskell/.cabal-sandbox + $TRAVIS_BUILD_DIR/../build/.cabal-sandbox # # Define the build matrix @@ -139,7 +138,7 @@ before_script: fi # use a minimal configuration for the haskell bindings to give it enough time to compile dependencies - if [[ $HASKELL == ON ]]; then bindings="cpp;haskell"; fi - - if [[ $HASKELL == ON ]]; then plugins="resolver_fm_hpu_b;dump;haskell;ini;sync;error;list;ipaddr;hosts"; fi + - if [[ $HASKELL == ON ]]; then plugins="resolver_fm_hpu_b;dump;haskell;ini;sync;error;list;ipaddr;hosts;spec"; fi - | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then python2_ver=$(python2 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') && \ diff --git a/cmake/Modules/LibAddHaskellPlugin.cmake b/cmake/Modules/LibAddHaskellPlugin.cmake index 848e0d3e59e..27881741545 100644 --- a/cmake/Modules/LibAddHaskellPlugin.cmake +++ b/cmake/Modules/LibAddHaskellPlugin.cmake @@ -171,9 +171,10 @@ macro (add_haskell_plugin target) DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" ) - set (SANDBOX_ADD_SOURCES "${ARG_SANDBOX_ADD_SOURCES};../../bindings/haskell/") + set (SANDBOX_ADD_SOURCES "${ARG_SANDBOX_ADD_SOURCES};") if (NOT ARG_NO_SHARED_SANDBOX) set (SHARED_SANDBOX "--sandbox;${CMAKE_BINARY_DIR}/.cabal-sandbox") + set (SANDBOX_ADD_SOURCES "${ARG_SANDBOX_ADD_SOURCES};../../bindings/haskell/") endif (NOT ARG_NO_SHARED_SANDBOX) execute_process (COMMAND ${CABAL_EXECUTABLE} sandbox init ${SHARED_SANDBOX} @@ -201,6 +202,8 @@ macro (add_haskell_plugin target) add_custom_command ( OUTPUT ${PLUGIN_HASKELL_NAME} # ensure any further dependencies added by plugin developers get installed to the sandbox + # TODO sometimes race conditions may occur, find a way to serialize this + # see https://github.com/haskell/cabal/issues/2220 COMMAND ${CABAL_EXECUTABLE} install --only-dependencies COMMAND ${CABAL_EXECUTABLE} ${CABAL_OPTS} configure COMMAND ${CABAL_EXECUTABLE} build @@ -258,12 +261,23 @@ macro (add_haskell_plugin target) set_target_properties (elektra-${target} PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_RPATH};${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}/elektra/haskell") + # the rpath for the dependencies while we are still in the build tree + add_custom_command (TARGET elektra-${target} POST_BUILD + COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \"${CMAKE_CURRENT_BINARY_DIR}/haskell\" \"$\" + COMMENT "Adding build-tree rpath for @{target}" + ) + # remove the rpath again after installing - cmake will add the actual rpath + install (CODE "execute_process (COMMAND ${CMAKE_INSTALL_NAME_TOOL} -delete_rpath + \"${CMAKE_CURRENT_BINARY_DIR}/haskell\" + \"${CMAKE_INSTALL_PREFIX}/lib${lib}/elektra/libelektra-${target}.so\" + OUTPUT_QUIET)" + ) endif (DEPENDENCY_PHASE AND (BUILD_SHARED OR BUILD_FULL)) if (ADDTESTING_PHASE AND BUILD_TESTING AND (BUILD_SHARED OR BUILD_FULL)) set_target_properties (testmod_${target} PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_RPATH};${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}/elektra/haskell") - # guide the tests to our haskell libraries + # guide the tests to our haskell libraries in a way it also works with the scripts set_property (TEST testmod_${target} PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_CURRENT_BINARY_DIR}/haskell") endif (ADDTESTING_PHASE AND BUILD_TESTING AND (BUILD_SHARED OR BUILD_FULL)) diff --git a/src/bindings/haskell/CMakeLists.txt b/src/bindings/haskell/CMakeLists.txt index 8ab6f7e5f70..f383fe46a19 100644 --- a/src/bindings/haskell/CMakeLists.txt +++ b/src/bindings/haskell/CMakeLists.txt @@ -55,20 +55,16 @@ if (NOT BUILD_STATIC) WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} OUTPUT_QUIET) add_custom_command ( OUTPUT ${BINDING_HASKELL_NAME} - COMMAND ${CABAL_EXECUTABLE} install --only-dependencies + # COMMAND ${CABAL_EXECUTABLE} install --only-dependencies COMMAND ${CABAL_EXECUTABLE} ${CABAL_OPTS} configure - # this only installs to the sandbox so plugins can depend on it - # without breaking the global db through shared sandboxes COMMAND ${CABAL_EXECUTABLE} build - # COMMAND ${CABAL_EXECUTABLE} install WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${BINDING_HASKELL_DEPENDENCIES} ${ELEKTRA_DEPENDENCY} ) add_custom_target (c2hs_haskell ALL DEPENDS "${BINDING_HASKELL_NAME}") if (BUILD_SHARED OR BUILD_FULL) - # install (FILES "${BINDING_HASKELL_NAME}" DESTINATION lib${LIB_SUFFIX}) # build and install it to the global db - # install (CODE "execute_process (COMMAND ${CABAL_EXECUTABLE} --ignore-sandbox install WORKING_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}\")") + install (CODE "execute_process (COMMAND ${CABAL_EXECUTABLE} --ignore-sandbox install WORKING_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}\")") endif (BUILD_SHARED OR BUILD_FULL) if (BUILD_TESTING) @@ -86,7 +82,7 @@ if (NOT BUILD_STATIC) OUTPUT ${HASKELL_TESTS} # they are getting installed to the sandbox, and they get cached to avoid reinstalling every time # disable everything that slows down compilation times of the dependencies to have reasonable travis build times - COMMAND ${CABAL_EXECUTABLE} install --enable-tests --only-dependencies + # COMMAND ${CABAL_EXECUTABLE} install --enable-tests --only-dependencies COMMAND ${CABAL_EXECUTABLE} ${CABAL_OPTS} --enable-tests configure COMMAND ${CABAL_EXECUTABLE} build WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} diff --git a/src/bindings/haskell/src/Elektra/Ease.chs b/src/bindings/haskell/src/Elektra/Ease.chs index 6f66a342342..161099e2a78 100644 --- a/src/bindings/haskell/src/Elektra/Ease.chs +++ b/src/bindings/haskell/src/Elektra/Ease.chs @@ -1,6 +1,6 @@ module Elektra.Ease ( - ArrayValidateNameResult (..), - arrayValidateName, arrayIncName, arrayGetNextKey, arrayGet, + ArrayValidateNameResult (..), + arrayValidateName, arrayIncName, arrayGetNextKey, arrayGet, keyGetRelativeName ) where diff --git a/src/plugins/haskell/haskell.c.in b/src/plugins/haskell/haskell.c.in index ca1f8557bd9..224b03e8fe0 100644 --- a/src/plugins/haskell/haskell.c.in +++ b/src/plugins/haskell/haskell.c.in @@ -123,8 +123,8 @@ Plugin * ELEKTRA_PLUGIN_EXPORT (@PLUGIN_NAME@) return elektraPluginExport ("@PLUGIN_NAME@", ELEKTRA_PLUGIN_OPEN, &elektraHaskellOpen, ELEKTRA_PLUGIN_CLOSE, &elektraHaskellClose, - ELEKTRA_PLUGIN_GET, &elektraHaskellGet, - ELEKTRA_PLUGIN_SET, &elektraHaskellSet, + ELEKTRA_PLUGIN_GET, &elektraHaskellGet, + ELEKTRA_PLUGIN_SET, &elektraHaskellSet, ELEKTRA_PLUGIN_ERROR, &elektraHaskellError, ELEKTRA_PLUGIN_END); } From 33860770ed2a0fc61afffafce10af8a8f8264986 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Thu, 28 Dec 2017 23:55:50 +0100 Subject: [PATCH 18/36] haskell-cabal-sandboxing: adjust travis file for after the rebase --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 71d15e0d0ce..e7932e74e5d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -138,7 +138,7 @@ before_script: fi # use a minimal configuration for the haskell bindings to give it enough time to compile dependencies - if [[ $HASKELL == ON ]]; then bindings="cpp;haskell"; fi - - if [[ $HASKELL == ON ]]; then plugins="resolver_fm_hpu_b;dump;haskell;ini;sync;error;list;ipaddr;hosts;spec"; fi + - if [[ $HASKELL == ON ]]; then plugins="resolver_fm_hpu_b;dump;haskell;ini;sync;error;spec;list;profile;regex;tracer;timeofday"; fi - | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then python2_ver=$(python2 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') && \ From 3268d6a1e2753b599fd1411ea1dd40499f5cefa4 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Fri, 29 Dec 2017 01:41:56 +0100 Subject: [PATCH 19/36] haskell-cabal-sandboxing: fix detection and installation issues on build --- cmake/Modules/LibAddHaskellPlugin.cmake | 6 ++-- src/bindings/haskell/CMakeLists.txt | 44 ++++++++++++++++--------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/cmake/Modules/LibAddHaskellPlugin.cmake b/cmake/Modules/LibAddHaskellPlugin.cmake index 27881741545..61340e975af 100644 --- a/cmake/Modules/LibAddHaskellPlugin.cmake +++ b/cmake/Modules/LibAddHaskellPlugin.cmake @@ -256,14 +256,15 @@ macro (add_haskell_plugin target) ${target} c2hs_haskell ADD_TEST ) - + if (TARGET elektra-${target}) if (DEPENDENCY_PHASE AND (BUILD_SHARED OR BUILD_FULL)) set_target_properties (elektra-${target} PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_RPATH};${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}/elektra/haskell") # the rpath for the dependencies while we are still in the build tree add_custom_command (TARGET elektra-${target} POST_BUILD - COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \"${CMAKE_CURRENT_BINARY_DIR}/haskell\" \"$\" + COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath + \"${CMAKE_CURRENT_BINARY_DIR}/haskell\" \"$\" COMMENT "Adding build-tree rpath for @{target}" ) # remove the rpath again after installing - cmake will add the actual rpath @@ -281,6 +282,7 @@ macro (add_haskell_plugin target) set_property (TEST testmod_${target} PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_CURRENT_BINARY_DIR}/haskell") endif (ADDTESTING_PHASE AND BUILD_TESTING AND (BUILD_SHARED OR BUILD_FULL)) + endif (TARGET elektra-${target}) mark_as_advanced ( GHC_FFI_LIB diff --git a/src/bindings/haskell/CMakeLists.txt b/src/bindings/haskell/CMakeLists.txt index f383fe46a19..e0128948308 100644 --- a/src/bindings/haskell/CMakeLists.txt +++ b/src/bindings/haskell/CMakeLists.txt @@ -82,7 +82,7 @@ if (NOT BUILD_STATIC) OUTPUT ${HASKELL_TESTS} # they are getting installed to the sandbox, and they get cached to avoid reinstalling every time # disable everything that slows down compilation times of the dependencies to have reasonable travis build times - # COMMAND ${CABAL_EXECUTABLE} install --enable-tests --only-dependencies + COMMAND ${CABAL_EXECUTABLE} install --enable-tests --only-dependencies COMMAND ${CABAL_EXECUTABLE} ${CABAL_OPTS} --enable-tests configure COMMAND ${CABAL_EXECUTABLE} build WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} @@ -101,24 +101,38 @@ if (NOT BUILD_STATIC) PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) endif (INSTALL_TESTING) - add_test (testhaskell_basic "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_basic/testhaskell_basic") - set_property (TEST testhaskell_basic PROPERTY LABELS bindings) - set_property (TEST testhaskell_basic PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib") + add_test (testhaskell_basic + "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_basic/testhaskell_basic") + set_property (TEST testhaskell_basic + PROPERTY LABELS bindings) + set_property (TEST testhaskell_basic + PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib") - add_test (testhaskell_basic_optimized "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_basic_optimized/testhaskell_basic_optimized") - set_property (TEST testhaskell_basic_optimized PROPERTY LABELS bindings) - set_property (TEST testhaskell_basic_optimized PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib") + add_test (testhaskell_basic_optimized + "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_basic_optimized/testhaskell_basic_optimized") + set_property (TEST testhaskell_basic_optimized + PROPERTY LABELS bindings) + set_property (TEST testhaskell_basic_optimized + PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib") if (ENABLE_KDB_TESTING) - add_test (testhaskell_realworld "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_realworld/testhaskell_realworld") - set_property (TEST testhaskell_realworld PROPERTY LABELS bindings) - set_property (TEST testhaskell_realworld APPEND PROPERTY LABELS kdbtests) - set_property (TEST testhaskell_realworld PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib") + add_test (testhaskell_realworld + "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_realworld/testhaskell_realworld") + set_property (TEST testhaskell_realworld + PROPERTY LABELS bindings) + set_property (TEST testhaskell_realworld + APPEND PROPERTY LABELS kdbtests) + set_property (TEST testhaskell_realworld + PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib") - add_test (testhaskell_realworld_optimized "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_realworld_optimized/testhaskell_realworld_optimized") - set_property (TEST testhaskell_realworld_optimized PROPERTY LABELS bindings) - set_property (TEST testhaskell_realworld_optimized APPEND PROPERTY LABELS kdbtests) - set_property (TEST testhaskell_realworld_optimized PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib") + add_test (testhaskell_realworld_optimized + "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_realworld_optimized/testhaskell_realworld_optimized") + set_property (TEST testhaskell_realworld_optimized + PROPERTY LABELS bindings) + set_property (TEST testhaskell_realworld_optimized + APPEND PROPERTY LABELS kdbtests) + set_property (TEST testhaskell_realworld_optimized + PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/lib") endif (ENABLE_KDB_TESTING) endif (BUILD_TESTING) else (NOT BUILD_STATIC) From 5d71bae6f5c81191b3d6834afa4dd674e4a24d42 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Fri, 29 Dec 2017 03:36:05 +0100 Subject: [PATCH 20/36] haskell-cabal-sandboxing: fix minimum build, fix missing placements metadata --- .travis.yml | 2 +- src/plugins/haskell/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e7932e74e5d..46084093f4c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -138,7 +138,7 @@ before_script: fi # use a minimal configuration for the haskell bindings to give it enough time to compile dependencies - if [[ $HASKELL == ON ]]; then bindings="cpp;haskell"; fi - - if [[ $HASKELL == ON ]]; then plugins="resolver_fm_hpu_b;dump;haskell;ini;sync;error;spec;list;profile;regex;tracer;timeofday"; fi + - if [[ $HASKELL == ON ]]; then plugins="rresolver_fm_hpu_b;dump;haskell;ini;sync;error;spec;list;network;profile;glob;tracer;timeofday"; fi - | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then python2_ver=$(python2 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') && \ diff --git a/src/plugins/haskell/README.md b/src/plugins/haskell/README.md index 039d0c4ee4a..34fe2b4a82b 100644 --- a/src/plugins/haskell/README.md +++ b/src/plugins/haskell/README.md @@ -4,7 +4,7 @@ - infos/needs = - infos/provides = - infos/recommends = -- infos/placements = +- infos/placements = getstorage setstorage - infos/status = maintained experimental - infos/metadata = - infos/description = base for haskell plugins From 2d715e665cee2c48fe2afd1e13ce54ff2823f4c8 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Fri, 29 Dec 2017 04:37:55 +0100 Subject: [PATCH 21/36] haskell-cabal-sandboxing: fix copy mistake --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 46084093f4c..d147df33a10 100644 --- a/.travis.yml +++ b/.travis.yml @@ -138,7 +138,7 @@ before_script: fi # use a minimal configuration for the haskell bindings to give it enough time to compile dependencies - if [[ $HASKELL == ON ]]; then bindings="cpp;haskell"; fi - - if [[ $HASKELL == ON ]]; then plugins="rresolver_fm_hpu_b;dump;haskell;ini;sync;error;spec;list;network;profile;glob;tracer;timeofday"; fi + - if [[ $HASKELL == ON ]]; then plugins="resolver_fm_hpu_b;dump;haskell;ini;sync;error;spec;list;network;profile;glob;tracer;timeofday"; fi - | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then python2_ver=$(python2 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') && \ From 5d876e21aeeaa4d8d529952d0e6e45e93f51c09a Mon Sep 17 00:00:00 2001 From: e1528532 Date: Fri, 29 Dec 2017 17:05:55 +0100 Subject: [PATCH 22/36] haskell-cabal-sandboxing: improve travis build config --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index d147df33a10..ec3e4ee61f1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -104,10 +104,10 @@ before_install: cabal update # avoid reinstalls if we already have them cached # disable everything that slows down compilation times of the dependencies to have reasonable travis build times + PATH=$PATH:"$HOME/.cabal/bin" which happy || cabal install happy which alex || cabal install alex which c2hs || cabal install c2hs - PATH=$PATH:"$HOME/.cabal/bin" fi - | if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then @@ -138,7 +138,8 @@ before_script: fi # use a minimal configuration for the haskell bindings to give it enough time to compile dependencies - if [[ $HASKELL == ON ]]; then bindings="cpp;haskell"; fi - - if [[ $HASKELL == ON ]]; then plugins="resolver_fm_hpu_b;dump;haskell;ini;sync;error;spec;list;network;profile;glob;tracer;timeofday"; fi + - if [[ $HASKELL == ON ]]; then plugins="resolver_fm_hpu_b;dump;haskell;ini;sync;error;spec;list;network;profile;glob;tracer;timeofday;binary;base64"; fi + - if [[ $HASKELL == ON ]]; then tools="kdb"; fi - | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then python2_ver=$(python2 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') && \ @@ -156,7 +157,7 @@ before_script: -DPLUGINS="${plugins:-ALL;-jni}" -DBINDINGS="${bindings:-ALL;}" -DENABLE_DEBUG=ON - -DTOOLS=ALL + -DTOOLS="${tools:-ALL;}" -DINSTALL_SYSTEM_FILES=OFF -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_INSTALL_PREFIX="$INSTALL_DIR" From 9283729394d68ac7c87a926df93ece1bfa936b77 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Sat, 30 Dec 2017 17:29:13 +0100 Subject: [PATCH 23/36] haskell-cabal-sandboxing: rollback cmakelists ordering --- src/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cb475eac63b..75de79a781e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,11 +1,12 @@ add_subdirectory (error) + set (ADDTESTING_PHASE OFF) -add_subdirectory (bindings) +add_subdirectory (plugins) add_subdirectory (libs) -add_subdirectory (plugins) +add_subdirectory (bindings) add_subdirectory (tools) From 088cbeec5c4d5b23a974fb1acd8496ca420fbdbd Mon Sep 17 00:00:00 2001 From: e1528532 Date: Sat, 30 Dec 2017 18:39:43 +0100 Subject: [PATCH 24/36] haskell-cabal-sandboxing: add fileheaders for haskell files --- src/bindings/haskell/examples/example_kdb.hs | 7 +++++++ src/bindings/haskell/examples/example_key.hs | 7 +++++++ src/bindings/haskell/examples/example_keyset.hs | 7 +++++++ src/bindings/haskell/src/Elektra/Ease.chs | 9 ++++++++- src/bindings/haskell/src/Elektra/KDB.chs | 11 ++++++++++- src/bindings/haskell/src/Elektra/Key.chs | 9 ++++++++- src/bindings/haskell/src/Elektra/KeySet.chs | 9 ++++++++- src/bindings/haskell/src/Elektra/Plugin.chs | 17 ++++++++++++----- src/bindings/haskell/test/Elektra.hs | 7 +++++++ src/bindings/haskell/test/ElektraRealWorld.hs | 7 +++++++ src/plugins/haskell/Elektra/Haskell.hs | 7 +++++++ 11 files changed, 88 insertions(+), 9 deletions(-) diff --git a/src/bindings/haskell/examples/example_kdb.hs b/src/bindings/haskell/examples/example_kdb.hs index f176901e3ad..52f60ac4bac 100644 --- a/src/bindings/haskell/examples/example_kdb.hs +++ b/src/bindings/haskell/examples/example_kdb.hs @@ -1,3 +1,10 @@ +-- +-- @file +-- +-- @brief KDB Haskell bindings examples +-- +-- @copyright BSD License (see LICENSE.md or https://www.libelektra.org) +-- module Example (main) where import Elektra.Key diff --git a/src/bindings/haskell/examples/example_key.hs b/src/bindings/haskell/examples/example_key.hs index 054e422a361..1f300b8963d 100644 --- a/src/bindings/haskell/examples/example_key.hs +++ b/src/bindings/haskell/examples/example_key.hs @@ -1,3 +1,10 @@ +-- +-- @file +-- +-- @brief Key Haskell bindings examples +-- +-- @copyright BSD License (see LICENSE.md or https://www.libelektra.org) +-- module Example (main) where import Elektra.Key diff --git a/src/bindings/haskell/examples/example_keyset.hs b/src/bindings/haskell/examples/example_keyset.hs index f313d28cf80..6d61901b45c 100644 --- a/src/bindings/haskell/examples/example_keyset.hs +++ b/src/bindings/haskell/examples/example_keyset.hs @@ -1,3 +1,10 @@ +-- +-- @file +-- +-- @brief KeySet Haskell bindings examples +-- +-- @copyright BSD License (see LICENSE.md or https://www.libelektra.org) +-- module Example (main) where import Elektra.Key diff --git a/src/bindings/haskell/src/Elektra/Ease.chs b/src/bindings/haskell/src/Elektra/Ease.chs index 161099e2a78..07100daaf7c 100644 --- a/src/bindings/haskell/src/Elektra/Ease.chs +++ b/src/bindings/haskell/src/Elektra/Ease.chs @@ -1,8 +1,15 @@ +-- +-- @file +-- +-- @brief LibEase Haskell bindings +-- +-- @copyright BSD License (see LICENSE.md or https://www.libelektra.org) +-- module Elektra.Ease ( ArrayValidateNameResult (..), arrayValidateName, arrayIncName, arrayGetNextKey, arrayGet, keyGetRelativeName - ) where +) where #include diff --git a/src/bindings/haskell/src/Elektra/KDB.chs b/src/bindings/haskell/src/Elektra/KDB.chs index 5de2a0278fe..b31d0d26999 100644 --- a/src/bindings/haskell/src/Elektra/KDB.chs +++ b/src/bindings/haskell/src/Elektra/KDB.chs @@ -1,4 +1,13 @@ -module Elektra.KDB (kdbOpen, kdbGet, kdbSet) where +-- +-- @file +-- +-- @brief KDB Haskell bindings +-- +-- @copyright BSD License (see LICENSE.md or https://www.libelektra.org) +-- +module Elektra.KDB ( + kdbOpen, kdbGet, kdbSet +) where {#import Elektra.Key#} {#import Elektra.KeySet#} diff --git a/src/bindings/haskell/src/Elektra/Key.chs b/src/bindings/haskell/src/Elektra/Key.chs index e155f83ede3..77a6cd3016f 100644 --- a/src/bindings/haskell/src/Elektra/Key.chs +++ b/src/bindings/haskell/src/Elektra/Key.chs @@ -1,3 +1,10 @@ +-- +-- @file +-- +-- @brief Key Haskell bindings +-- +-- @copyright BSD License (see LICENSE.md or https://www.libelektra.org) +-- module Elektra.Key ( Key (..), Namespace (..), ElektraKeyVarargs (KeyMetaName, KeyBinary, KeyComment, KeyOwner), keyNew, keyNewWithValue, keyNewWithFlagsAndValue, @@ -15,7 +22,7 @@ module Elektra.Key ( keyIsBelow, keyIsDirectBelow, keyRel, keyIsInactive, keyIsBinary, keyIsString, keyPtrNull, ifKey, withKey - ) where +) where #include import Foreign.Marshal.Alloc (allocaBytes) diff --git a/src/bindings/haskell/src/Elektra/KeySet.chs b/src/bindings/haskell/src/Elektra/KeySet.chs index 4d9151f650d..c9092530c80 100644 --- a/src/bindings/haskell/src/Elektra/KeySet.chs +++ b/src/bindings/haskell/src/Elektra/KeySet.chs @@ -1,3 +1,10 @@ +-- +-- @file +-- +-- @brief KeySet Haskell bindings +-- +-- @copyright BSD License (see LICENSE.md or https://www.libelektra.org) +-- module Elektra.KeySet ( KeySet (..), LookupOptions (..), ElektraCursor, withKeySet, @@ -7,7 +14,7 @@ module Elektra.KeySet ( ksRewind, ksNext, ksCurrent, ksHead, ksTail, ksList, ksGetCursor, ksSetCursor, ksAtCursor, ksLookup, ksLookupO, ksLookupByName, ksLookupByNameO - ) where +) where {#import Elektra.Key#} import Control.Monad (liftM, liftM2) diff --git a/src/bindings/haskell/src/Elektra/Plugin.chs b/src/bindings/haskell/src/Elektra/Plugin.chs index 7b87f2f7096..a6f8a5af49b 100644 --- a/src/bindings/haskell/src/Elektra/Plugin.chs +++ b/src/bindings/haskell/src/Elektra/Plugin.chs @@ -1,3 +1,10 @@ +-- +-- @file +-- +-- @brief Plugin Haskell bindings +-- +-- @copyright BSD License (see LICENSE.md or https://www.libelektra.org) +-- module Elektra.Plugin ( Plugin, PluginStatus (..), elektraPluginGetConfig, @@ -5,7 +12,7 @@ module Elektra.Plugin ( elektraPluginOpenWith, elektraPluginCloseWith, elektraPluginGetWith, elektraPluginSetWith, elektraPluginErrorWith, elektraPluginCheckConfigWith - ) where +) where {#import Elektra.Key#} {#import Elektra.KeySet#} @@ -23,13 +30,13 @@ import Control.Monad (join, liftM, liftM2, liftM3) data PluginStatus = Error | NoUpdate | Success deriving (Show, Eq) instance Enum PluginStatus where - fromEnum Error = -1 + fromEnum Error = -1 fromEnum NoUpdate = 0 - fromEnum Success = 1 + fromEnum Success = 1 toEnum (-1) = Error - toEnum 0 = NoUpdate - toEnum 1 = Success + toEnum 0 = NoUpdate + toEnum 1 = Success toEnum unmatched = error ("PluginStatus.toEnum: Cannot match " ++ show unmatched) -- *** diff --git a/src/bindings/haskell/test/Elektra.hs b/src/bindings/haskell/test/Elektra.hs index 7e7064c841a..20adecaff8e 100644 --- a/src/bindings/haskell/test/Elektra.hs +++ b/src/bindings/haskell/test/Elektra.hs @@ -1,3 +1,10 @@ +-- +-- @file +-- +-- @brief Key and KeySet Haskell binding tests +-- +-- @copyright BSD License (see LICENSE.md or https://www.libelektra.org) +-- module Main (main) where import Elektra.Key diff --git a/src/bindings/haskell/test/ElektraRealWorld.hs b/src/bindings/haskell/test/ElektraRealWorld.hs index 99b8e5f3956..5cc290a15e5 100644 --- a/src/bindings/haskell/test/ElektraRealWorld.hs +++ b/src/bindings/haskell/test/ElektraRealWorld.hs @@ -1,3 +1,10 @@ +-- +-- @file +-- +-- @brief KDB Haskell binding tests +-- +-- @copyright BSD License (see LICENSE.md or https://www.libelektra.org) +-- module Main (main) where import Elektra.Key diff --git a/src/plugins/haskell/Elektra/Haskell.hs b/src/plugins/haskell/Elektra/Haskell.hs index ca853faee85..29443b1f123 100644 --- a/src/plugins/haskell/Elektra/Haskell.hs +++ b/src/plugins/haskell/Elektra/Haskell.hs @@ -1,3 +1,10 @@ +-- +-- @file +-- +-- @brief Minimum Haskell plugin example +-- +-- @copyright BSD License (see LICENSE.md or https://www.libelektra.org) +-- module Elektra.Haskell where import Elektra.Key From 7e8d7390e94743b54e4685fe7332a9a58a488143 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Wed, 3 Jan 2018 18:12:50 +0100 Subject: [PATCH 25/36] haskell-cabal-sandboxing: fix concurrency issues with sandboxes, fix setup logic for older cabal versions --- cmake/Modules/FindHaskell.cmake | 10 +- cmake/Modules/LibAddHaskellPlugin.cmake | 99 ++++++++++++++----- src/bindings/haskell/CMakeLists.txt | 12 ++- .../haskell/libelektra-haskell.cabal.in | 2 +- src/bindings/haskell/src/Elektra/KDB.chs | 2 +- src/plugins/haskell/Setup.hs | 42 +++++--- src/plugins/haskell/haskell.cabal.in | 2 +- 7 files changed, 121 insertions(+), 48 deletions(-) diff --git a/cmake/Modules/FindHaskell.cmake b/cmake/Modules/FindHaskell.cmake index 1bc12c1f0a1..ef970a40553 100644 --- a/cmake/Modules/FindHaskell.cmake +++ b/cmake/Modules/FindHaskell.cmake @@ -79,14 +79,14 @@ if (GHC-PKG_EXECUTABLE) set (CABAL_DYNLIB_PATH "${CABAL_LOCATION}/lib/${GHC_DYNAMIC_LIBRARY_DIR}") # dependencies for the default cmake Setup.hs - set (CABAL_CUSTOM_TARGET + set (CABAL_CUSTOM_SETUP "custom-setup setup-depends: - Cabal >= 2.0 && < 2.1, - containers >= 0.5 && < 0.6, + Cabal >= 1.24 && < 2.1, + containers >= 0.4 && < 0.6, base >= 4.7 && < 5 , - directory >= 1.2 && < 1.4, - process >= 1.6 && < 1.7, + directory >= 1.1 && < 1.4, + process >= 1.2 && < 1.7, filepath >= 1.3 && < 1.5") # By using cabal sandboxes we can install hspec and QuickCheck to the sandbox without diff --git a/cmake/Modules/LibAddHaskellPlugin.cmake b/cmake/Modules/LibAddHaskellPlugin.cmake index 61340e975af..7601976a858 100644 --- a/cmake/Modules/LibAddHaskellPlugin.cmake +++ b/cmake/Modules/LibAddHaskellPlugin.cmake @@ -4,7 +4,6 @@ include (LibAddMacros) # libraries automatically. # # Expects that plugins make use of cabal as their build system. -# Expects that # # MODULES: # the name of the haskell modules to be compiled @@ -177,14 +176,11 @@ macro (add_haskell_plugin target) set (SANDBOX_ADD_SOURCES "${ARG_SANDBOX_ADD_SOURCES};../../bindings/haskell/") endif (NOT ARG_NO_SHARED_SANDBOX) - execute_process (COMMAND ${CABAL_EXECUTABLE} sandbox init ${SHARED_SANDBOX} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} OUTPUT_QUIET) - if (SANDBOX_ADD_SOURCES) - foreach (SANDBOX_ADD_SOURCE ${SANDBOX_ADD_SOURCES}) - execute_process (COMMAND ${CABAL_EXECUTABLE} sandbox add-source "${SANDBOX_ADD_SOURCE}" - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - endforeach() - endif (SANDBOX_ADD_SOURCES) + configure_haskell_sandbox ( + SHARED_SANDBOX ${SHARED_SANDBOX} + SANDBOX_ADD_SOURCES ${SANDBOX_ADD_SOURCES} + DEPENDS c2hs_haskell + ) # Grab potential haskell source files file (GLOB_RECURSE PLUGIN_SOURCE_FILES @@ -201,23 +197,18 @@ macro (add_haskell_plugin target) add_custom_command ( OUTPUT ${PLUGIN_HASKELL_NAME} - # ensure any further dependencies added by plugin developers get installed to the sandbox - # TODO sometimes race conditions may occur, find a way to serialize this - # see https://github.com/haskell/cabal/issues/2220 - COMMAND ${CABAL_EXECUTABLE} install --only-dependencies COMMAND ${CABAL_EXECUTABLE} ${CABAL_OPTS} configure COMMAND ${CABAL_EXECUTABLE} build - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS c2hs_haskell - "${CMAKE_SOURCE_DIR}/src/plugins/haskell/haskell.c.in" - "${CMAKE_SOURCE_DIR}/src/plugins/haskell/haskell.h.in" - "${CMAKE_SOURCE_DIR}/src/plugins/haskell/Setup.hs" + "${CMAKE_SOURCE_DIR}/src/plugins/haskell/Setup.hs" ${PLUGIN_SOURCE_FILES} + ${HASKELL_ADD_SOURCES_TARGET} ) add_custom_target (${target} ALL DEPENDS ${PLUGIN_HASKELL_NAME}) if (BUILD_SHARED OR BUILD_FULL) - install (DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/haskell" + install (DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/haskell" DESTINATION "lib${LIB_SUFFIX}/elektra/") endif (BUILD_SHARED OR BUILD_FULL) @@ -264,13 +255,12 @@ macro (add_haskell_plugin target) # the rpath for the dependencies while we are still in the build tree add_custom_command (TARGET elektra-${target} POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath - \"${CMAKE_CURRENT_BINARY_DIR}/haskell\" \"$\" - COMMENT "Adding build-tree rpath for @{target}" + \"${CMAKE_CURRENT_BINARY_DIR}/haskell\" \"$\" ) # remove the rpath again after installing - cmake will add the actual rpath install (CODE "execute_process (COMMAND ${CMAKE_INSTALL_NAME_TOOL} -delete_rpath \"${CMAKE_CURRENT_BINARY_DIR}/haskell\" - \"${CMAKE_INSTALL_PREFIX}/lib${lib}/elektra/libelektra-${target}.so\" + \"${CMAKE_INSTALL_PREFIX}/lib${lib}/elektra/libelektra-${target}.so\" OUTPUT_QUIET)" ) endif (DEPENDENCY_PHASE AND (BUILD_SHARED OR BUILD_FULL)) @@ -279,7 +269,7 @@ macro (add_haskell_plugin target) PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_RPATH};${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}/elektra/haskell") # guide the tests to our haskell libraries in a way it also works with the scripts - set_property (TEST testmod_${target} PROPERTY ENVIRONMENT + set_property (TEST testmod_${target} PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_CURRENT_BINARY_DIR}/haskell") endif (ADDTESTING_PHASE AND BUILD_TESTING AND (BUILD_SHARED OR BUILD_FULL)) endif (TARGET elektra-${target}) @@ -292,3 +282,68 @@ macro (add_haskell_plugin target) GHC_PRIM_LIB ) endmacro (add_haskell_plugin) + +# Allows adding sandbox sources for haskell plugins which will be executed in a serial manner +# to avoid cabal concurrency issues https://github.com/haskell/cabal/issues/2220. Also initializes +# sandboxes and will install the dependencies into them. +# +# SANDBOX_ADD_SOURCES: +# additional source paths which should be added to the cabal sandbox +# required if the build should depend on haskell libraries not available on hackage +# WORKING_DIRECTORY: +# in case your plugin depends on other files than *.hs and *.lhs haskell files and the default +# cabal file and c test file and setup file, you can specify them here +# DEPENDS: +# additional targets this call should be dependent on +# +macro (configure_haskell_sandbox) + cmake_parse_arguments (ARG + "" # optional keywords + "" # one value keywords + "SANDBOX_ADD_SOURCES;DEPENDS" # multi value keywords + ${ARGN} + ) + + get_property (HASKELL_SANDBOX_DEP_IDX GLOBAL PROPERTY HASKELL_SANDBOX_DEP_IDX) + if (NOT HASKELL_SANDBOX_DEP_IDX) + set (HASKELL_SANDBOX_DEP_IDX 1) + add_custom_command (OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/cabal.sandbox.config" + COMMAND ${CABAL_EXECUTABLE} sandbox init ${SHARED_SANDBOX} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${ARG_DEPENDS} + ) + set (HASKELL_ADD_SOURCES_TARGET haskell-add-sources-${HASKELL_SANDBOX_DEP_IDX}) + add_custom_target (${HASKELL_ADD_SOURCES_TARGET} ALL DEPENDS + "${CMAKE_CURRENT_BINARY_DIR}/cabal.sandbox.config") + else (NOT HASKELL_SANDBOX_DEP_IDX) + add_custom_command (OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/cabal.sandbox.config" + COMMAND ${CABAL_EXECUTABLE} sandbox init ${SHARED_SANDBOX} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS haskell-add-sources-${HASKELL_SANDBOX_DEP_IDX} + ${ARG_DEPENDS} + ) + math (EXPR HASKELL_SANDBOX_DEP_IDX "${HASKELL_SANDBOX_DEP_IDX} + 1") + set (HASKELL_ADD_SOURCES_TARGET haskell-add-sources-${HASKELL_SANDBOX_DEP_IDX}) + add_custom_target (${HASKELL_ADD_SOURCES_TARGET} ALL DEPENDS + "${CMAKE_CURRENT_BINARY_DIR}/cabal.sandbox.config") + endif (NOT HASKELL_SANDBOX_DEP_IDX) + + if (ARG_SANDBOX_ADD_SOURCES) + foreach (SANDBOX_ADD_SOURCE ${ARG_SANDBOX_ADD_SOURCES}) + add_custom_command (OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/cabal.sandbox.config" + COMMAND ${CABAL_EXECUTABLE} sandbox add-source "${SANDBOX_ADD_SOURCE}" + APPEND + ) + endforeach (SANDBOX_ADD_SOURCE ${ARG_SANDBOX_ADD_SOURCES}) + endif (ARG_SANDBOX_ADD_SOURCES) + file (WRITE "${CMAKE_CURRENT_BINARY_DIR}/cabalOptionalDependencies.cmake" + "execute_process (COMMAND ${CABAL_EXECUTABLE} install --only-dependencies)") + add_custom_command (OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/cabal.sandbox.config" + # ensure any further dependencies added by plugin developers get installed to the sandbox + COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cabalOptionalDependencies.cmake" + APPEND + ) + + set_property (GLOBAL PROPERTY HASKELL_SANDBOX_DEP_IDX "${HASKELL_SANDBOX_DEP_IDX}") +endmacro (configure_haskell_sandbox) + diff --git a/src/bindings/haskell/CMakeLists.txt b/src/bindings/haskell/CMakeLists.txt index e0128948308..0abe29e3080 100644 --- a/src/bindings/haskell/CMakeLists.txt +++ b/src/bindings/haskell/CMakeLists.txt @@ -50,12 +50,13 @@ if (NOT BUILD_STATIC) "${CMAKE_CURRENT_SOURCE_DIR}/test/ElektraRealWorld.hs" "${CMAKE_SOURCE_DIR}/src/plugins/haskell/Setup.hs" ) - + file (WRITE "${CMAKE_CURRENT_BINARY_DIR}/cabalOptionalDependencies.cmake" + "execute_process (COMMAND ${CABAL_EXECUTABLE} install --only-dependencies)") execute_process (COMMAND ${CABAL_EXECUTABLE} sandbox init --sandbox ${CMAKE_BINARY_DIR}/.cabal-sandbox WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} OUTPUT_QUIET) add_custom_command ( OUTPUT ${BINDING_HASKELL_NAME} - # COMMAND ${CABAL_EXECUTABLE} install --only-dependencies + COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cabalOptionalDependencies.cmake" COMMAND ${CABAL_EXECUTABLE} ${CABAL_OPTS} configure COMMAND ${CABAL_EXECUTABLE} build WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} @@ -78,11 +79,12 @@ if (NOT BUILD_STATIC) "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_realworld/testhaskell_realworld" "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_realworld_optimized/testhaskell_realworld_optimized" ) + file (WRITE "${CMAKE_CURRENT_BINARY_DIR}/cabalOptionalDependencies.cmake" + "execute_process (COMMAND ${CABAL_EXECUTABLE} install --enable-tests --only-dependencies)") add_custom_command ( OUTPUT ${HASKELL_TESTS} - # they are getting installed to the sandbox, and they get cached to avoid reinstalling every time - # disable everything that slows down compilation times of the dependencies to have reasonable travis build times - COMMAND ${CABAL_EXECUTABLE} install --enable-tests --only-dependencies + # everything is getting installed to the sandbox, and gets cached to avoid reinstalling every time + COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cabalOptionalDependencies.cmake" COMMAND ${CABAL_EXECUTABLE} ${CABAL_OPTS} --enable-tests configure COMMAND ${CABAL_EXECUTABLE} build WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} diff --git a/src/bindings/haskell/libelektra-haskell.cabal.in b/src/bindings/haskell/libelektra-haskell.cabal.in index d84f5942574..15095807b79 100644 --- a/src/bindings/haskell/libelektra-haskell.cabal.in +++ b/src/bindings/haskell/libelektra-haskell.cabal.in @@ -9,7 +9,7 @@ maintainer: e1528532@libelektra.org copyright: libelektra.org category: binding build-type: Custom -cabal-version: >=1.10 +cabal-version: >=1.24 @CABAL_CUSTOM_SETUP@ diff --git a/src/bindings/haskell/src/Elektra/KDB.chs b/src/bindings/haskell/src/Elektra/KDB.chs index b31d0d26999..df8c21607ec 100644 --- a/src/bindings/haskell/src/Elektra/KDB.chs +++ b/src/bindings/haskell/src/Elektra/KDB.chs @@ -6,7 +6,7 @@ -- @copyright BSD License (see LICENSE.md or https://www.libelektra.org) -- module Elektra.KDB ( - kdbOpen, kdbGet, kdbSet + kdbOpen, kdbGet, kdbSet ) where {#import Elektra.Key#} diff --git a/src/plugins/haskell/Setup.hs b/src/plugins/haskell/Setup.hs index 81c74767ed9..c18c3e815bf 100644 --- a/src/plugins/haskell/Setup.hs +++ b/src/plugins/haskell/Setup.hs @@ -20,18 +20,23 @@ import System.Exit (exitFailure) import System.Process (readProcess) import System.Directory (createDirectoryIfMissing) -import qualified Data.Set as S -import qualified Distribution.Simple.Compiler as C -import qualified Distribution.Simple.Setup as C -import qualified Distribution.Types.LocalBuildInfo as C -import qualified Distribution.Types.PackageDescription as C -import qualified Distribution.Package as C -import qualified Distribution.Simple.LocalBuildInfo as C -import qualified Distribution.Simple.PackageIndex as C -import qualified Distribution.Text as C -import qualified Distribution.Simple.Program as C -import qualified Distribution.Verbosity as V -import qualified Distribution.InstalledPackageInfo as IPI +import qualified Data.Set as S +import qualified Distribution.Simple.Compiler as C +import qualified Distribution.Simple.Setup as C +#if MIN_VERSION_Cabal(2,0,0) +import qualified Distribution.Types.LocalBuildInfo as C +import qualified Distribution.Types.ComponentLocalBuildInfo as C +import qualified Distribution.Types.PackageDescription as C +#else +import qualified Distribution.PackageDescription as C +import qualified Distribution.Simple.LocalBuildInfo as C +#endif +import qualified Distribution.Package as C +import qualified Distribution.Simple.PackageIndex as C +import qualified Distribution.Text as C +import qualified Distribution.Simple.Program as C +import qualified Distribution.Verbosity as V +import qualified Distribution.InstalledPackageInfo as IPI #if MIN_VERSION_Cabal(1,24,0) type PackageIndex a = C.PackageIndex IPI.InstalledPackageInfo @@ -110,9 +115,16 @@ findTransitiveDependencies pkgIdx initial = go S.empty (S.toList initial) | otherwise = maybe (go set qs) (go (S.insert q set) . (++ qs) . IPI.depends) $ C.lookupUnitId pkgIdx q +getComponents :: C.LocalBuildInfo -> [C.ComponentLocalBuildInfo] +#if MIN_VERSION_Cabal(2,0,0) +getComponents = toList . C.componentGraph +#else +getComponents = map (\(_, clbi, _) -> clbi) . C.componentsConfigs +#endif + getDependencyInstalledPackageIds :: C.LocalBuildInfo -> Set C.UnitId getDependencyInstalledPackageIds lbi = findTransitiveDependencies (C.installedPkgs lbi) $ - S.fromList [ ipId | componentLbi <- toList (C.componentGraph lbi) + S.fromList [ ipId | componentLbi <- getComponents lbi , (ipId, _) <- C.componentPackageDeps componentLbi] getDependencyInstalledPackageInfos :: C.LocalBuildInfo -> [InstalledPackageInfo] @@ -125,7 +137,11 @@ showLibrary lbi ipi = fromJust (getLibraryDir lbi ipi) ++ "/libHS" ++ getLibrary getLibraryDir :: C.LocalBuildInfo -> InstalledPackageInfo -> Maybe String getLibraryDir lbi ipi = listToMaybe $ case buildType lbi of Static -> filter (getLibraryName ipi `isSuffixOf`) $ IPI.libraryDirs ipi + #if MIN_VERSION_Cabal(1,24,1) Dynamic -> IPI.libraryDynDirs ipi + #else + Dynamic -> IPI.libraryDirs ipi + #endif getLibraryName :: InstalledPackageInfo -> String getLibraryName = C.display . IPI.installedUnitId diff --git a/src/plugins/haskell/haskell.cabal.in b/src/plugins/haskell/haskell.cabal.in index 41eeee80143..2c537b97ff3 100644 --- a/src/plugins/haskell/haskell.cabal.in +++ b/src/plugins/haskell/haskell.cabal.in @@ -9,7 +9,7 @@ maintainer: e1528532@libelektra.org copyright: libelektra.org category: plugin build-type: Custom -cabal-version: >=1.10 +cabal-version: >=1.24 @CABAL_CUSTOM_SETUP@ From 76986e40faf19f0bca4e7d38c77dcffe70903d18 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Thu, 11 Jan 2018 23:33:53 +0100 Subject: [PATCH 26/36] haskell-cabal-sandboxing: add dini to the travis configuration --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ec3e4ee61f1..fdc13cdd986 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ cache: $HOME/.m2 $HOME/.cabal $HOME/Library/Haskell - $TRAVIS_BUILD_DIR/../build/.cabal-sandbox + $TRAVIS_BUILD_DIR/.cabal-sandbox # # Define the build matrix @@ -138,7 +138,7 @@ before_script: fi # use a minimal configuration for the haskell bindings to give it enough time to compile dependencies - if [[ $HASKELL == ON ]]; then bindings="cpp;haskell"; fi - - if [[ $HASKELL == ON ]]; then plugins="resolver_fm_hpu_b;dump;haskell;ini;sync;error;spec;list;network;profile;glob;tracer;timeofday;binary;base64"; fi + - if [[ $HASKELL == ON ]]; then plugins="resolver_fm_hpu_b;dump;haskell;ini;dini;sync;error;spec;list;network;profile;glob;tracer;timeofday;binary;base64"; fi - if [[ $HASKELL == ON ]]; then tools="kdb"; fi - | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then From e3501929aee689c0903810e86e4c24dac29e9227 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Fri, 12 Jan 2018 00:11:18 +0100 Subject: [PATCH 27/36] haskell-cabal-sandboxing: adjust remove_binding which is now called differently --- src/bindings/haskell/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bindings/haskell/CMakeLists.txt b/src/bindings/haskell/CMakeLists.txt index 0abe29e3080..5ac9330bab8 100644 --- a/src/bindings/haskell/CMakeLists.txt +++ b/src/bindings/haskell/CMakeLists.txt @@ -138,8 +138,8 @@ if (NOT BUILD_STATIC) endif (ENABLE_KDB_TESTING) endif (BUILD_TESTING) else (NOT BUILD_STATIC) - remove_binding (haskell "BUILD_STATIC is currently not compatible with the haskell bindings") + exclude_binding (haskell "BUILD_STATIC is currently not compatible with the haskell bindings") endif (NOT BUILD_STATIC) else (HASKELL_FOUND) - remove_binding (haskell ${HASKELL_NOTFOUND_INFO}) + exclude_binding (haskell ${HASKELL_NOTFOUND_INFO}) endif (HASKELL_FOUND) From bf9894d19bc80b32102b0a6750d72b131c980d3c Mon Sep 17 00:00:00 2001 From: e1528532 Date: Fri, 12 Jan 2018 01:34:53 +0100 Subject: [PATCH 28/36] haskell-cabal-sandboxing: revert travis change --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fdc13cdd986..2f07a2afd55 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ cache: $HOME/.m2 $HOME/.cabal $HOME/Library/Haskell - $TRAVIS_BUILD_DIR/.cabal-sandbox + $TRAVIS_BUILD_DIR/../build/.cabal-sandbox # # Define the build matrix From 67ab46a857ec47d0c81895a7f732b33051509a35 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Fri, 12 Jan 2018 20:51:36 +0100 Subject: [PATCH 29/36] haskell-cabal-sandboxing: don't let the haskell plugin write to stdout --- src/plugins/haskell/Elektra/Haskell.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/plugins/haskell/Elektra/Haskell.hs b/src/plugins/haskell/Elektra/Haskell.hs index 29443b1f123..6f0d92d0f04 100644 --- a/src/plugins/haskell/Elektra/Haskell.hs +++ b/src/plugins/haskell/Elektra/Haskell.hs @@ -13,27 +13,27 @@ import Elektra.Plugin import Foreign.Ptr elektraHaskellOpen :: Plugin -> Key -> IO PluginStatus -elektraHaskellOpen p k = putStrLn "haskell elektraHaskellOpen" >> return Success +elektraHaskellOpen p k = keySetMeta k "/plugins/haskell" "elektraHaskellOpen" >> return Success hs_elektraHaskellOpen = elektraPluginOpenWith elektraHaskellOpen elektraHaskellClose :: Plugin -> Key -> IO PluginStatus -elektraHaskellClose p k = putStrLn "haskell elektraHaskellClose" >> return Success +elektraHaskellClose p k = keySetMeta k "/plugins/haskell" "elektraHaskellClose" >> return Success hs_elektraHaskellClose = elektraPluginCloseWith elektraHaskellClose elektraHaskellGet :: Plugin -> KeySet -> Key -> IO PluginStatus -elektraHaskellGet p ks k = putStrLn "haskell elektraHaskellGet" >> return NoUpdate +elektraHaskellGet p ks k = keySetMeta k "/plugins/haskell" "elektraHaskellGet" >> return NoUpdate hs_elektraHaskellGet = elektraPluginGetWith elektraHaskellGet elektraHaskellSet :: Plugin -> KeySet -> Key -> IO PluginStatus -elektraHaskellSet p ks k = putStrLn "haskell elektraHaskellSet" >> return NoUpdate +elektraHaskellSet p ks k = keySetMeta k "/plugins/haskell" "elektraHaskellSet" >> return NoUpdate hs_elektraHaskellSet = elektraPluginSetWith elektraHaskellSet elektraHaskellError :: Plugin -> KeySet -> Key -> IO PluginStatus -elektraHaskellError p ks k = putStrLn "haskell elektraHaskellError" >> return Success +elektraHaskellError p ks k = keySetMeta k "/plugins/haskell" "elektraHaskellError" >> return Success hs_elektraHaskellError = elektraPluginErrorWith elektraHaskellError elektraHaskellCheckConfig :: Key -> KeySet -> IO PluginStatus -elektraHaskellCheckConfig k ks = putStrLn "haskell elektraHaskellCheckConfig" >> return Success +elektraHaskellCheckConfig k ks = keySetMeta k "/plugins/haskell" "elektraHaskellCheckConfig" >> return Success hs_elektraHaskellCheckConfig = elektraPluginCheckConfigWith elektraHaskellCheckConfig foreign export ccall hs_elektraHaskellOpen :: Ptr Plugin -> Ptr Key -> IO Int From 1259c4d8e45f5c93f3ff9ffbfa5793412655286b Mon Sep 17 00:00:00 2001 From: e1528532 Date: Fri, 12 Jan 2018 21:11:58 +0100 Subject: [PATCH 30/36] haskell-cabal-sandboxing: supress cabal output by default --- cmake/Modules/LibAddHaskellPlugin.cmake | 12 ++++++------ src/bindings/haskell/CMakeLists.txt | 16 ++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cmake/Modules/LibAddHaskellPlugin.cmake b/cmake/Modules/LibAddHaskellPlugin.cmake index 7601976a858..8b671c824ce 100644 --- a/cmake/Modules/LibAddHaskellPlugin.cmake +++ b/cmake/Modules/LibAddHaskellPlugin.cmake @@ -197,8 +197,8 @@ macro (add_haskell_plugin target) add_custom_command ( OUTPUT ${PLUGIN_HASKELL_NAME} - COMMAND ${CABAL_EXECUTABLE} ${CABAL_OPTS} configure - COMMAND ${CABAL_EXECUTABLE} build + COMMAND ${CABAL_EXECUTABLE} ${CABAL_OPTS} configure -v0 + COMMAND ${CABAL_EXECUTABLE} build -v0 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS c2hs_haskell "${CMAKE_SOURCE_DIR}/src/plugins/haskell/Setup.hs" @@ -308,7 +308,7 @@ macro (configure_haskell_sandbox) if (NOT HASKELL_SANDBOX_DEP_IDX) set (HASKELL_SANDBOX_DEP_IDX 1) add_custom_command (OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/cabal.sandbox.config" - COMMAND ${CABAL_EXECUTABLE} sandbox init ${SHARED_SANDBOX} + COMMAND ${CABAL_EXECUTABLE} sandbox init ${SHARED_SANDBOX} -v0 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${ARG_DEPENDS} ) @@ -317,7 +317,7 @@ macro (configure_haskell_sandbox) "${CMAKE_CURRENT_BINARY_DIR}/cabal.sandbox.config") else (NOT HASKELL_SANDBOX_DEP_IDX) add_custom_command (OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/cabal.sandbox.config" - COMMAND ${CABAL_EXECUTABLE} sandbox init ${SHARED_SANDBOX} + COMMAND ${CABAL_EXECUTABLE} sandbox init ${SHARED_SANDBOX} -v0 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS haskell-add-sources-${HASKELL_SANDBOX_DEP_IDX} ${ARG_DEPENDS} @@ -331,13 +331,13 @@ macro (configure_haskell_sandbox) if (ARG_SANDBOX_ADD_SOURCES) foreach (SANDBOX_ADD_SOURCE ${ARG_SANDBOX_ADD_SOURCES}) add_custom_command (OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/cabal.sandbox.config" - COMMAND ${CABAL_EXECUTABLE} sandbox add-source "${SANDBOX_ADD_SOURCE}" + COMMAND ${CABAL_EXECUTABLE} sandbox add-source "${SANDBOX_ADD_SOURCE}" -v0 APPEND ) endforeach (SANDBOX_ADD_SOURCE ${ARG_SANDBOX_ADD_SOURCES}) endif (ARG_SANDBOX_ADD_SOURCES) file (WRITE "${CMAKE_CURRENT_BINARY_DIR}/cabalOptionalDependencies.cmake" - "execute_process (COMMAND ${CABAL_EXECUTABLE} install --only-dependencies)") + "execute_process (COMMAND ${CABAL_EXECUTABLE} install --only-dependencies -v0)") add_custom_command (OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/cabal.sandbox.config" # ensure any further dependencies added by plugin developers get installed to the sandbox COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cabalOptionalDependencies.cmake" diff --git a/src/bindings/haskell/CMakeLists.txt b/src/bindings/haskell/CMakeLists.txt index 5ac9330bab8..639f54c9ed9 100644 --- a/src/bindings/haskell/CMakeLists.txt +++ b/src/bindings/haskell/CMakeLists.txt @@ -51,21 +51,21 @@ if (NOT BUILD_STATIC) "${CMAKE_SOURCE_DIR}/src/plugins/haskell/Setup.hs" ) file (WRITE "${CMAKE_CURRENT_BINARY_DIR}/cabalOptionalDependencies.cmake" - "execute_process (COMMAND ${CABAL_EXECUTABLE} install --only-dependencies)") - execute_process (COMMAND ${CABAL_EXECUTABLE} sandbox init --sandbox ${CMAKE_BINARY_DIR}/.cabal-sandbox + "execute_process (COMMAND ${CABAL_EXECUTABLE} install --only-dependencies -v0)") + execute_process (COMMAND ${CABAL_EXECUTABLE} sandbox init --sandbox ${CMAKE_BINARY_DIR}/.cabal-sandbox -v0 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} OUTPUT_QUIET) add_custom_command ( OUTPUT ${BINDING_HASKELL_NAME} COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cabalOptionalDependencies.cmake" - COMMAND ${CABAL_EXECUTABLE} ${CABAL_OPTS} configure - COMMAND ${CABAL_EXECUTABLE} build + COMMAND ${CABAL_EXECUTABLE} ${CABAL_OPTS} configure -v0 + COMMAND ${CABAL_EXECUTABLE} build -v0 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${BINDING_HASKELL_DEPENDENCIES} ${ELEKTRA_DEPENDENCY} ) add_custom_target (c2hs_haskell ALL DEPENDS "${BINDING_HASKELL_NAME}") if (BUILD_SHARED OR BUILD_FULL) # build and install it to the global db - install (CODE "execute_process (COMMAND ${CABAL_EXECUTABLE} --ignore-sandbox install WORKING_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}\")") + install (CODE "execute_process (COMMAND ${CABAL_EXECUTABLE} --ignore-sandbox install -v0 WORKING_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}\")") endif (BUILD_SHARED OR BUILD_FULL) if (BUILD_TESTING) @@ -80,13 +80,13 @@ if (NOT BUILD_STATIC) "${CMAKE_CURRENT_BINARY_DIR}/dist/build/testhaskell_realworld_optimized/testhaskell_realworld_optimized" ) file (WRITE "${CMAKE_CURRENT_BINARY_DIR}/cabalOptionalDependencies.cmake" - "execute_process (COMMAND ${CABAL_EXECUTABLE} install --enable-tests --only-dependencies)") + "execute_process (COMMAND ${CABAL_EXECUTABLE} install --enable-tests --only-dependencies -v0)") add_custom_command ( OUTPUT ${HASKELL_TESTS} # everything is getting installed to the sandbox, and gets cached to avoid reinstalling every time COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cabalOptionalDependencies.cmake" - COMMAND ${CABAL_EXECUTABLE} ${CABAL_OPTS} --enable-tests configure - COMMAND ${CABAL_EXECUTABLE} build + COMMAND ${CABAL_EXECUTABLE} ${CABAL_OPTS} --enable-tests configure -v0 + COMMAND ${CABAL_EXECUTABLE} build -v0 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${BINDING_HASKELL_DEPENDENCIES} ${ELEKTRA_DEPENDENCY} c2hs_haskell ) From a0cdac5dd193b688efd95e142019316354f98787 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Fri, 12 Jan 2018 23:08:40 +0100 Subject: [PATCH 31/36] haskell-cabal-sandboxing: add release note --- doc/news/_preparation_next_release.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/news/_preparation_next_release.md b/doc/news/_preparation_next_release.md index fd0e7c21a37..bd71c4bd5f8 100644 --- a/doc/news/_preparation_next_release.md +++ b/doc/news/_preparation_next_release.md @@ -93,6 +93,8 @@ We added even more functionality, which could not make it to the highlights: - Elektra is now part of the official [Homebrew repository](http://formulae.brew.sh/formula/elektra). We still provide a [tap](http://github.com/ElektraInitiative/homebrew-elektra), if you want to install Elektra together with plugins or bindings that require additional libraries. +- The building and linking of the haskell bindings and haskell plugins has been +[greatly improved](https://github.com/ElektraInitiative/libelektra/pull/1698). ## Documentation From 0ff004fd5b483d6be9bbfa40a5b9c92e68bf03ed Mon Sep 17 00:00:00 2001 From: e1528532 Date: Mon, 15 Jan 2018 13:19:09 +0100 Subject: [PATCH 32/36] haskell-cabal-sandboxing: add hosts plugin to minimal config --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2f07a2afd55..34c1a44cf9f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -138,7 +138,7 @@ before_script: fi # use a minimal configuration for the haskell bindings to give it enough time to compile dependencies - if [[ $HASKELL == ON ]]; then bindings="cpp;haskell"; fi - - if [[ $HASKELL == ON ]]; then plugins="resolver_fm_hpu_b;dump;haskell;ini;dini;sync;error;spec;list;network;profile;glob;tracer;timeofday;binary;base64"; fi + - if [[ $HASKELL == ON ]]; then plugins="resolver_fm_hpu_b;dump;ini;dini;sync;error;hosts;list;glob;profile;spec;network;tracer;timeofday;binary;base64;haskell"; fi - if [[ $HASKELL == ON ]]; then tools="kdb"; fi - | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then From c6a256d86b1793754142487e84d78673d49fbef8 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Tue, 16 Jan 2018 16:57:45 +0100 Subject: [PATCH 33/36] haskell-cabal-sandboxing: fix potential build issue with glib --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 34c1a44cf9f..f3562634a97 100644 --- a/.travis.yml +++ b/.travis.yml @@ -88,6 +88,7 @@ before_install: brew install gobject-introspection brew install libgcrypt brew install libgit2 + brew install libuv brew install lua brew install openssl brew install python @@ -116,6 +117,7 @@ before_install: sudo apt-get install ninja-build sudo apt-get install libboost-all-dev sudo apt-get install libyaml-cpp-dev + sudo apt-get install libuv-dev fi # From 5d2b8db8d55aecd1fc6373f9635191ea12a9c357 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Tue, 16 Jan 2018 21:52:11 +0100 Subject: [PATCH 34/36] haskell-cabal-sandboxing: revert travis bindings change --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f3562634a97..e59c3ebbbf0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -157,7 +157,7 @@ before_script: -GNinja -DBUILD_STATIC=OFF -DPLUGINS="${plugins:-ALL;-jni}" - -DBINDINGS="${bindings:-ALL;}" + -DBINDINGS="${bindings}" -DENABLE_DEBUG=ON -DTOOLS="${tools:-ALL;}" -DINSTALL_SYSTEM_FILES=OFF From 5bdb8d7bd38e9262e7ce61f8bcfbc847913e601b Mon Sep 17 00:00:00 2001 From: e1528532 Date: Thu, 18 Jan 2018 19:07:08 +0100 Subject: [PATCH 35/36] haskell-cabal-sandboxing: cleanup the realworld test traces to fix kdb run_all --- src/bindings/haskell/test/ElektraRealWorld.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bindings/haskell/test/ElektraRealWorld.hs b/src/bindings/haskell/test/ElektraRealWorld.hs index 5cc290a15e5..9067b699852 100644 --- a/src/bindings/haskell/test/ElektraRealWorld.hs +++ b/src/bindings/haskell/test/ElektraRealWorld.hs @@ -26,6 +26,10 @@ main = hspec $ ksAfter <- ksNew 1 kdbOpen parent $ \kdbAfter -> do _ <- kdbGet kdbAfter ksAfter parent - ksLookupByName ksAfter haskellPersisted >>= keyName >>= (`shouldBe` haskellPersisted) + keyAfter <- ksLookupByNameO ksAfter haskellPersisted KdbOPop + keyName keyAfter >>= (`shouldBe` haskellPersisted) + -- Now set the kdb to the initial state again with the key popped + _ <- kdbSet kdbAfter ksAfter parent + return () where haskellPersisted = "user/tests/testhaskell_cabal/haskellPersisted" From c3f731e4b104dc991311377cf72550c8db77c7b1 Mon Sep 17 00:00:00 2001 From: e1528532 Date: Thu, 18 Jan 2018 19:09:33 +0100 Subject: [PATCH 36/36] haskell-cabal-sandboxing: exclude cpp bindings from haskell build (not required), remove nonexistent plugin --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e59c3ebbbf0..39d3b23471a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -139,8 +139,8 @@ before_script: CMAKE_OPT+=("-DCOMMON_FLAGS=-Werror") fi # use a minimal configuration for the haskell bindings to give it enough time to compile dependencies - - if [[ $HASKELL == ON ]]; then bindings="cpp;haskell"; fi - - if [[ $HASKELL == ON ]]; then plugins="resolver_fm_hpu_b;dump;ini;dini;sync;error;hosts;list;glob;profile;spec;network;tracer;timeofday;binary;base64;haskell"; fi + - if [[ $HASKELL == ON ]]; then bindings="haskell"; fi + - if [[ $HASKELL == ON ]]; then plugins="resolver_fm_hpu_b;dump;ini;dini;sync;error;hosts;list;glob;profile;spec;network;tracer;timeofday;base64;haskell"; fi - if [[ $HASKELL == ON ]]; then tools="kdb"; fi - | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then