packages feed

zoovisitor 0.2.5.1 → 0.2.6.1

raw patch · 8 files changed

+304/−82 lines, 8 filesdep ~asyncdep ~bytestringdep ~hspecPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: async, bytestring, hspec, uuid

API changes (from Hackage documentation)

+ ZooKeeper: StdErrLogStream :: StdLogStream
+ ZooKeeper: StdOutLogStream :: StdLogStream
+ ZooKeeper: closeFileLogStream :: FileLogStream -> IO ()
+ ZooKeeper: data FileLogStream
+ ZooKeeper: data StdLogStream
+ ZooKeeper: openFileLogStream :: CBytes -> CBytes -> IO FileLogStream
+ ZooKeeper: withZooSetFileLogStream :: CBytes -> CBytes -> IO a -> IO a
+ ZooKeeper: zooSetStdLogStream :: StdLogStream -> IO ()
+ ZooKeeper.Types: pattern ZooLogSilence :: ZooLogLevel
+ ZooKeeper.Types: pattern ZooNotconnectedState :: ZooState
+ ZooKeeper.Types: pattern ZooReadonlyState :: ZooState

Files

cbits/hs_zk.c view
@@ -390,6 +390,18 @@ }  // ----------------------------------------------------------------------------++void hs_zoo_set_std_log_stream(int s) {+  if (s == 1) {+    zoo_set_log_stream(stdout);+  } else if (s == 2) {+    zoo_set_log_stream(stderr);+  } else {+    fprintf(stderr, "hs_zoo_set_std_log_stream: invalid %d\n", s);+  }+}++// ---------------------------------------------------------------------------- // Helpers  const stat_t* dup_stat(const stat_t* old_stat) {@@ -399,7 +411,7 @@ }  const string_vector_t* dup_string_vector(const string_vector_t* old_strings) {-  int count = old_strings->count;+  int32_t count = old_strings->count;   if (count < 0) {     fprintf(stderr, "dup_string_vector error: count %d\n", count);     return NULL;@@ -407,7 +419,7 @@   string_vector_t* new_strings =       (string_vector_t*)malloc(sizeof(string_vector_t));   char** vals = malloc(count * sizeof(char*));-  for (int i = 0; i < count; ++i) {+  for (int32_t i = 0; i < count; ++i) {     vals[i] = strdup(old_strings->data[i]);   }   new_strings->count = count;@@ -415,15 +427,28 @@   return new_strings; } +void free_string_vector(string_vector_t* strings) {+  if (strings == NULL || strings->data == NULL) {+    return;+  }++  for (int32_t i = 0; i < strings->count; ++i) {+    free(strings->data[i]);+  }+  free(strings->data);+  strings->data = NULL;+  free(strings);+}+ const acl_vector_t* dup_acl_vector(const acl_vector_t* old_acls) {-  int count = old_acls->count;+  int32_t count = old_acls->count;   if (count < 0) {     fprintf(stderr, "dup_acl_vector error: count %d\n", count);     return NULL;   }   acl_t* data = (acl_t*)malloc(count * sizeof(acl_t));   acl_t* old_data = old_acls->data;-  for (int i = 0; i < count; ++i) {+  for (int32_t i = 0; i < count; ++i) {     data[i].perms = old_data[i].perms;     data[i].id.scheme = strdup(old_data[i].id.scheme);     data[i].id.id = strdup(old_data[i].id.id);@@ -433,4 +458,16 @@   new_acls->count = count;   new_acls->data = data;   return new_acls;+}++void free_acl_vector(acl_vector_t* acls) {+  if (acls == NULL || acls->data == NULL) {+    return;+  }+  for (int32_t i = 0; i < acls->count; ++i) {+    free(acls->data[i].id.id);+    free(acls->data[i].id.scheme);+  }+  free(acls->data);+  free(acls); }
src/ZooKeeper.hs view
@@ -1,11 +1,16 @@ module ZooKeeper   ( I.zooVersion-  , I.zooSetDebugLevel    , zookeeperResInit   , U.withResource   , U.Resource +  , I.zooSetDebugLevel+  , StdLogStream (..)+  , zooSetStdLogStream+  , FileLogStream+  , withZooSetFileLogStream+   , zooCreate   , zooCreateIfMissing   , zooSet@@ -34,16 +39,18 @@   , zooRecvTimeout   , isUnrecoverable -    -- * Internal function (should NOT be used)+    -- * Internal function   , zookeeperInit   , zookeeperClose+  , openFileLogStream+  , closeFileLogStream   ) where -import Control.Exception (catch)+import           Control.Exception        (bracket, catch, throwIO) import           Control.Monad            (void, when, zipWithM, (<=<)) import           Data.Bifunctor           (first) import           Data.Maybe               (fromMaybe)-import           Foreign.C                (CInt)+import           Foreign.C                (CFile, CInt) import           Foreign.Ptr              (FunPtr, Ptr, freeHaskellFunPtr,                                            nullFunPtr, nullPtr, plusPtr) import           GHC.Stack                (HasCallStack)@@ -747,3 +754,89 @@ -- value may change after a server re-connect. zooRecvTimeout :: T.ZHandle -> IO CInt zooRecvTimeout = I.c_zoo_recv_timeout++data StdLogStream = StdOutLogStream | StdErrLogStream++newtype FileLogStream = FileLogStream (Ptr CFile)++zooSetStdLogStream :: StdLogStream -> IO ()+zooSetStdLogStream StdOutLogStream = I.hs_zoo_set_std_log_stream 1+zooSetStdLogStream StdErrLogStream = I.hs_zoo_set_std_log_stream 2++-- | Sets the file to be used by the library for logging.+--+-- The file will be closed after the computation you run, and the LogStream+-- will be set to defaut stderr.+--+-- > let res = zookeeperResInit "127.0.0.1:2181" Nothing 5000 Nothing 0+-- > withZooSetFileLogStream "/tmp/zk.log" "a+" $ do+-- >   withResource res $ \zh -> do+-- >     undefined+withZooSetFileLogStream+  :: CBytes+  -- ^ File name, see 'openFileLogStream'+  -> CBytes+  -- ^ Mode, see 'openFileLogStream'+  -> IO a+  -- ^ Computation to run in-between+  -> IO a+withZooSetFileLogStream filename mode = bracket acquire release . const+  where+    acquire = do+      new@(FileLogStream new') <- openFileLogStream filename mode+      I.c_zoo_set_log_stream new'+      pure new+    -- FIXME: If zookeeper export the get LogStream api, then I can set back+    -- to the original LogStream but the stderr.+    release s = I.hs_zoo_set_std_log_stream 2 >> closeFileLogStream s++-- | Open the file by filename using the given mode.+--+-- The operation may fail with IOError if open filed.+--+-- Also see: <https://man7.org/linux/man-pages/man3/fopen.3.html>+openFileLogStream+  :: CBytes+  -- ^ File name+  -> CBytes+  -- ^ Mode+  --+  -- A string beginning with one of the following sequences:+  --+  -- r      Open text file for reading. The stream is positioned at+  --        the beginning of the file.+  --+  -- r+     Open for reading and writing. The stream is positioned at+  --        the beginning of the file.+  --+  -- w      Truncate file to zero length or create text file for+  --        writing.  The stream is positioned at the beginning of the+  --        file.+  --+  -- w+     Open for reading and writing. The file is created if it+  --        does not exist, otherwise it is truncated. The stream is+  --        positioned at the beginning of the file.+  --+  -- a      Open for appending (writing at end of file). The file is+  --        created if it does not exist. The stream is positioned at+  --        the end of the file.+  --+  -- a+     Open for reading and appending (writing at end of file).+  --        The file is created if it does not exist. Output is+  --        always appended to the end of the file.  POSIX is silent+  --        on what the initial read position is when using this mode.+  --        For glibc, the initial file position for reading is at the+  --        beginning of the file, but for Android/BSD/MacOS, the+  --        initial file position for reading is at the end of the+  --        file.+  -> IO FileLogStream+openFileLogStream filename mode =+  CBytes.withCBytesUnsafe filename $ \filename' ->+  CBytes.withCBytesUnsafe mode $ \mode' -> do+    cfilep <- I.c_fopen filename' mode'+    if (cfilep == nullPtr)+       then throwIO $ userError ("Open file " <> CBytes.unpack filename <> " failed!")+       else pure $ FileLogStream cfilep++closeFileLogStream :: FileLogStream -> IO ()+closeFileLogStream (FileLogStream p) = I.c_fflush p >> void (I.c_fclose p)
src/ZooKeeper/Internal/FFI.hsc view
@@ -7,8 +7,7 @@ import           Control.Concurrent import           Control.Exception import           Control.Monad                (void)-import           Data.Version                 (Version, makeVersion,-                                               parseVersion)+import           Data.Version                 (Version, makeVersion) import           Data.Word import           Foreign.C import           Foreign.ForeignPtr@@ -16,7 +15,6 @@ import           Foreign.StablePtr import           GHC.Conc import           GHC.Stack                    (HasCallStack)-import           Text.ParserCombinators.ReadP (readP_to_S) import qualified Z.Data.CBytes                as CBytes import           Z.Foreign @@ -25,18 +23,25 @@  #include "hs_zk.h" +#ifndef ZOO_MAJOR_VERSION+import           Data.Version                 (parseVersion)+import           Text.ParserCombinators.ReadP (readP_to_S)+#endif+ -------------------------------------------------------------------------------  zooVersion :: Version #ifdef ZOO_MAJOR_VERSION+-- For zookeeper-3.4 zooVersion = makeVersion [ (#const ZOO_MAJOR_VERSION)                          , (#const ZOO_MINOR_VERSION)                          , (#const ZOO_PATCH_VERSION)                          ] #else+-- For zookeeper-3.6+ zooVersion = case readP_to_S parseVersion (#const_str ZOO_VERSION) of                [_, _, (r, _)] -> r-               otherwise      -> makeVersion [0, 0, 0]  -- unsupported+               _otherwise     -> makeVersion [0, 0, 0]  -- unsupported #endif  foreign import ccall unsafe "hs_zk.h &logLevel"@@ -46,6 +51,12 @@ foreign import ccall unsafe "hs_zk.h zoo_set_debug_level"   zooSetDebugLevel :: ZooLogLevel -> IO () +foreign import capi unsafe "zookeeper/zookeeper.h zoo_set_log_stream"+  c_zoo_set_log_stream :: Ptr CFile -> IO ()++foreign import ccall unsafe "hs_zoo_set_std_log_stream"+  hs_zoo_set_std_log_stream :: CInt -> IO ()+ foreign import ccall "wrapper"   mkCWatcherFnPtr :: CWatcherFn -> IO (FunPtr CWatcherFn) @@ -234,6 +245,17 @@  foreign import ccall unsafe "zookeeper.h is_unrecoverable"   c_is_unrecoverable :: ZHandle -> IO CInt++-------------------------------------------------------------------------------++foreign import capi unsafe "stdio.h fopen"+  c_fopen :: BA## Word8 -> BA## Word8 -> IO (Ptr CFile)++foreign import capi unsafe "stdio.h fclose"+  c_fclose :: Ptr CFile -> IO CInt++foreign import capi unsafe "stdio.h fflush"+  c_fflush :: Ptr CFile -> IO CInt  ------------------------------------------------------------------------------- -- Helpers
src/ZooKeeper/Internal/Types.hsc view
@@ -3,7 +3,7 @@  module ZooKeeper.Internal.Types where -import           Control.Exception     (bracket_)+import           Control.Exception     (finally) import           Control.Monad         (forM) import           Data.ByteString.Short (ShortByteString) import qualified Data.ByteString.Short as BShort (packCStringLen)@@ -64,6 +64,10 @@ pattern ZooLogInfo  = ZooLogLevel (#const ZOO_LOG_LEVEL_INFO) pattern ZooLogDebug = ZooLogLevel (#const ZOO_LOG_LEVEL_DEBUG) +-- | Disable logging+pattern ZooLogSilence :: ZooLogLevel+pattern ZooLogSilence = ZooLogLevel 0+ -------------------------------------------------------------------------------  -- | ACL permissions.@@ -111,29 +115,38 @@   { aclPerms    :: [ZooPerm]   , aclIdScheme :: CBytes   , aclId       :: CBytes-  } deriving Show+  } deriving (Show, Eq) -{-# INLINE sizeOfZooAcl #-} sizeOfZooAcl :: Int sizeOfZooAcl = (#size acl_t)+{-# INLINE sizeOfZooAcl #-} -peekZooAcl :: Ptr ZooAcl -> IO ZooAcl-peekZooAcl ptr = do-  perms <- toZooPerms <$> (#peek acl_t, perms) ptr-  scheme_ptr <- (#peek acl_t, id.scheme) ptr-  id_ptr <- (#peek acl_t, id.id) ptr-  scheme <- CBytes.fromCString scheme_ptr <* free scheme_ptr-  acl_id <- CBytes.fromCString id_ptr <* free id_ptr+newtype AclVector = AclVector (Ptr ())+  deriving (Show, Eq)++peekAclVector :: AclVector -> IO [ZooAcl]+peekAclVector p@(AclVector ptr) = flip finally (free_acl_vector p) $ do+  count <- fromIntegral @Int32 <$> (#peek acl_vector_t, count) ptr+  data_ptr <- (#peek acl_vector_t, data) ptr+  forM [0..count-1] $ peekAclVectorIdx data_ptr++peekAclVectorIdx :: Ptr ZooAcl -> Int -> IO ZooAcl+peekAclVectorIdx ptr offset = do+  let ptr' = ptr `plusPtr` (offset * sizeOfZooAcl)+  perms <- toZooPerms <$> (#peek acl_t, perms) ptr'+  scheme_ptr <- (#peek acl_t, id.scheme) ptr'+  id_ptr <- (#peek acl_t, id.id) ptr'+  scheme <- CBytes.fromCString scheme_ptr+  acl_id <- CBytes.fromCString id_ptr   return $ ZooAcl perms scheme acl_id  -- TODO unsafeAllocaZooAcl :: ZooAcl -> IO Z.ByteArray unsafeAllocaZooAcl = undefined --- FIXME: consider this--- data AclVector = AclVector (Ptr ()) | AclList [ZooAcl]-newtype AclVector = AclVector (Ptr ())-  deriving (Show, Eq)+-- TODO+fromAclList :: [ZooAcl] -> IO AclVector+fromAclList = undefined  -- | This is a completely open ACL foreign import ccall unsafe "hs_zk.h &ZOO_OPEN_ACL_UNSAFE"@@ -147,17 +160,8 @@ foreign import ccall unsafe "hs_zk.h &ZOO_CREATOR_ALL_ACL"   zooCreatorAllAcl :: AclVector -toAclList :: AclVector -> IO [ZooAcl]-toAclList (AclVector ptr) = do-  count <- fromIntegral @Int32 <$> (#peek acl_vector_t, count) ptr-  data_ptr <- (#peek acl_vector_t, data) ptr-  forM [0..count-1] $ \idx -> do-    let data_ptr' = data_ptr `plusPtr` (idx * sizeOfZooAcl)-    peekZooAcl data_ptr'---- TODO-fromAclList :: [ZooAcl] -> IO AclVector-fromAclList = undefined+foreign import ccall unsafe "free_acl_vector"+  free_acl_vector :: AclVector -> IO ()  ------------------------------------------------------------------------------- @@ -193,12 +197,14 @@   deriving newtype (Text.Print)  instance Show ZooState where-  show ZooExpiredSession   = "ExpiredSession"-  show ZooAuthFailed       = "AuthFailed"-  show ZooConnectingState  = "ConnectingState"-  show ZooAssociatingState = "AssociatingState"-  show ZooConnectedState   = "ConnectedState"-  show (ZooState x)        = "ZooState " <> show x+  show ZooExpiredSession    = "ExpiredSession"+  show ZooAuthFailed        = "AuthFailed"+  show ZooConnectingState   = "ConnectingState"+  show ZooAssociatingState  = "AssociatingState"+  show ZooConnectedState    = "ConnectedState"+  show ZooReadonlyState     = "ReadonlyState"+  show ZooNotconnectedState = "NotconnectedState"+  show (ZooState x)         = "ZooState " <> show x  pattern     ZooExpiredSession, ZooAuthFailed@@ -209,12 +215,29 @@ pattern ZooAssociatingState = ZooState (#const ZOO_ASSOCIATING_STATE) pattern ZooConnectedState   = ZooState (#const ZOO_CONNECTED_STATE) --- TODO--- pattern ZOO_READONLY_STATE :: ZooState--- pattern ZOO_READONLY_STATE = ZooState (#const ZOO_READONLY_STATE)--- pattern ZOO_NOTCONNECTED_STATE :: ZooState--- pattern ZOO_NOTCONNECTED_STATE = ZooState (#const ZOO_NOTCONNECTED_STATE)+-- This a trick to determine whether the C library expose the following apis.+--+-- ZOO_VERSION was introduced by ZOOKEEPER-3635 (3.6.0-pre). Also version after+-- 3.6 exports the following states.+#ifdef ZOO_VERSION +pattern ZooReadonlyState :: ZooState+pattern ZooReadonlyState = ZooState (#const ZOO_READONLY_STATE)++pattern ZooNotconnectedState :: ZooState+pattern ZooNotconnectedState = ZooState (#const ZOO_NOTCONNECTED_STATE)++#else+-- Hardcode for zookeeper-3.4++pattern ZooReadonlyState :: ZooState+pattern ZooReadonlyState = ZooState 5++pattern ZooNotconnectedState :: ZooState+pattern ZooNotconnectedState = ZooState 999++#endif+ -------------------------------------------------------------------------------  -- | Watch Types@@ -280,32 +303,35 @@ newtype CreateMode = CreateMode { unCreateMode :: CInt }   deriving (Show, Eq, Storable) --- TODO: The following C constants (such as ZOO_PERSISTENT, ZOO_PERSISTENT_SEQUENTIAL---       and ZOO_EPTHMERAL_SEQUENTIAL) are not defined on clients <= 3.4.x. However,---       they can be used by passing an integer directly.------       The hard-coded ones will be replaced with the following patterns which use---       C constants when the library only support clients >= 3.5.x.----pattern ZooPersistent :: CreateMode---pattern ZooPersistent = CreateMode (#const ZOO_PERSISTENT)------pattern ZooPersistentSequential :: CreateMode---pattern ZooPersistentSequential = CreateMode (#const ZOO_PERSISTENT_SEQUENTIAL)------pattern ZooEphemeralSequential :: CreateMode---pattern ZooEphemeralSequential = CreateMode (#const ZOO_EPHEMERAL_SEQUENTIAL)------pattern ZooContainer :: CreateMode---pattern ZooContainer = CreateMode (#const ZOO_CONTAINER)+-- This a trick to determine whether the C library expose the following apis. -----pattern ZooPersistentWithTTL :: CreateMode---pattern ZooPersistentWithTTL = CreateMode (#const ZOO_PERSISTENT_WITH_TTL)+-- ZOO_VERSION was introduced by ZOOKEEPER-3635 (3.6.0-pre). -----pattern ZooPersistentSequentialWithTTL :: CreateMode---pattern ZooPersistentSequentialWithTTL = CreateMode (#const ZOO_PERSISTENT_SEQUENTIAL_WITH_TTL)+-- The following C constants (such as ZOO_PERSISTENT, ZOO_PERSISTENT_SEQUENTIAL+-- and ZOO_EPTHMERAL_SEQUENTIAL) are not defined on clients <= 3.4.x.+#ifdef ZOO_VERSION  pattern ZooPersistent :: CreateMode+pattern ZooPersistent = CreateMode (#const ZOO_PERSISTENT)++pattern ZooPersistentSequential :: CreateMode+pattern ZooPersistentSequential = CreateMode (#const ZOO_PERSISTENT_SEQUENTIAL)++pattern ZooEphemeralSequential :: CreateMode+pattern ZooEphemeralSequential = CreateMode (#const ZOO_EPHEMERAL_SEQUENTIAL)++pattern ZooContainer :: CreateMode+pattern ZooContainer = CreateMode (#const ZOO_CONTAINER)++pattern ZooPersistentWithTTL :: CreateMode+pattern ZooPersistentWithTTL = CreateMode (#const ZOO_PERSISTENT_WITH_TTL)++pattern ZooPersistentSequentialWithTTL :: CreateMode+pattern ZooPersistentSequentialWithTTL = CreateMode (#const ZOO_PERSISTENT_SEQUENTIAL_WITH_TTL)++#else++pattern ZooPersistent :: CreateMode pattern ZooPersistent = CreateMode 0  pattern ZooPersistentSequential :: CreateMode@@ -323,6 +349,8 @@ pattern ZooPersistentSequentialWithTTL :: CreateMode pattern ZooPersistentSequentialWithTTL = CreateMode 6 +#endif+ -- | The znode will be deleted upon the client's disconnect. pattern ZooEphemeral :: CreateMode pattern ZooEphemeral = CreateMode (#const ZOO_EPHEMERAL)@@ -367,8 +395,9 @@ newtype StringVector = StringVector { unStrVec :: [CBytes] }   deriving Show +-- Peek a StringVector from point and then free the pointer peekStringVector :: Ptr StringVector -> IO StringVector-peekStringVector ptr = bracket_ (return ()) (free ptr) $ do+peekStringVector ptr = flip finally (free_string_vector ptr) $ do   -- NOTE: Int32 is necessary, since count is int32_t in c   count <- fromIntegral @Int32 <$> (#peek string_vector_t, count) ptr   StringVector <$> forM [0..count-1] (peekStringVectorIdx ptr)@@ -377,8 +406,11 @@ peekStringVectorIdx ptr offset = do   ptr' <- (#peek string_vector_t, data) ptr   data_ptr <- peek $ ptr' `plusPtr` (offset * (sizeOf ptr'))-  CBytes.fromCString data_ptr <* free data_ptr+  CBytes.fromCString data_ptr  -- this will do a copy +foreign import ccall unsafe "free_string_vector"+  free_string_vector :: Ptr StringVector -> IO ()+ ------------------------------------------------------------------------------- -- Callback datas @@ -492,7 +524,7 @@   csize = (#size hs_acl_completion_t)   peekRet ptr = (#peek hs_acl_completion_t, rc) ptr   peekData ptr = do-    acls <- toAclList . AclVector =<< (#peek hs_acl_completion_t, acl) ptr+    acls <- peekAclVector . AclVector =<< (#peek hs_acl_completion_t, acl) ptr     stat_ptr <- (#peek hs_acl_completion_t, stat) ptr     stat <- peekStat stat_ptr     return $ AclCompletion acls stat
src/ZooKeeper/Internal/Utils.hs view
@@ -1,6 +1,6 @@ module ZooKeeper.Internal.Utils   ( -- * Resource management-    Resource(..)+    Resource (..)   , initResource   , initResource_   , withResource
src/ZooKeeper/Types.hs view
@@ -39,6 +39,8 @@   , pattern I.ZooConnectingState   , pattern I.ZooAssociatingState   , pattern I.ZooConnectedState+  , pattern I.ZooReadonlyState+  , pattern I.ZooNotconnectedState    , I.CreateMode   , pattern I.ZooPersistent@@ -55,6 +57,7 @@   , pattern I.ZooLogWarn   , pattern I.ZooLogInfo   , pattern I.ZooLogDebug+  , pattern I.ZooLogSilence    , I.ZooPerm   , pattern I.ZooPermRead
test/Spec.hs view
@@ -38,6 +38,7 @@ opSpec zh = do   describe "ZooKeeper.zooVersion" $ do     it "version should be 3.4.* - 3.8.*" $ do+      putStrLn $ "Tested with zooVersion: " <> show zooVersion       zooVersion `shouldSatisfy` (>= makeVersion [3, 4, 0])       zooVersion `shouldSatisfy` (<= makeVersion [3, 8, 0]) 
zoovisitor.cabal view
@@ -1,6 +1,6 @@ cabal-version:      2.2 name:               zoovisitor-version:            0.2.5.1+version:            0.2.6.1 synopsis:   A haskell binding to Apache Zookeeper C library(mt) using Haskell Z project. @@ -12,7 +12,7 @@ copyright:          Copyright (c) author:             mu maintainer:         mu@laxcat.xyz-tested-with:        GHC ==8.10.7 || ==9.0.2 || ==9.2.8+tested-with:        GHC ==8.10.7 || ==9.0.2 || ==9.2.8 || ==9.4.5 category:           Database homepage:           https://github.com/ZHaskell/zoovisitor bug-reports:        https://github.com/ZHaskell/zoovisitor/issues@@ -27,6 +27,26 @@   type:     git   location: https://github.com/ZHaskell/zoovisitor +-- XXX: Hackage requires 'cabal-version' must be at most 3.0.+-- But the flag 'zoovisitor_enable_asan' use 'hsc2hs-options' requres >= 3.6.+-- flag zoovisitor_enable_asan+--   default:     False+--   description:+--     Enable AddressSanitizer. This is only for local debug usage.+--     Also, do not forget to set cabal-version to 3.6 manually.++-- XXX: require cabal-version >= 3.6+-- Tricky options to link static archive, see: https://github.com/haskell/cabal/issues/4677+-- common link-asan+--   if os(osx)+--     ghc-options: "-optl-Wl,-lasan"+--+--   if !os(osx)+--     ghc-options:+--       -pgml g++ "-optl-Wl,--allow-multiple-definition"+--       "-optl-Wl,--whole-archive" "-optl-Wl,-Bstatic" "-optl-Wl,-lasan"+--       "-optl-Wl,-Bdynamic" "-optl-Wl,--no-whole-archive"+ library   hs-source-dirs:     src   exposed-modules:@@ -45,15 +65,25 @@    build-depends:     , base        >=4.12      && <5-    , bytestring  >=0.10.10.0+    , bytestring  >=0.10.10.0 && <0.13     , exceptions  ^>=0.10-    , Z-Data      >=0.7.2     && <1.5 || ^>=2.0+    , Z-Data      >=0.7.2     && <1.5  || ^>=2.0    includes:           hs_zk.h   c-sources:          cbits/hs_zk.c   include-dirs:       include /usr/local/include   build-tool-depends: hsc2hs:hsc2hs   extra-libraries:    zookeeper_mt++  -- XXX: require cabal-version >= 3.6+  -- if flag(zoovisitor_enable_asan)+  --   cc-options:+  --     -fsanitize=address -fno-omit-frame-pointer -static-libasan++  --   hsc2hs-options:+  --     "--cflag=-fsanitize=address" "--lflag=-fsanitize=address"+  --     "--cflag=-static-libasan" "--lflag=-static-libasan"+   default-language:   Haskell2010   default-extensions:     BangPatterns@@ -80,14 +110,18 @@     -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints  test-suite zoovisitor-test+  -- XXX: require cabal-version >= 3.6+  -- if flag(zoovisitor_enable_asan)+  --   import: link-asan+   type:             exitcode-stdio-1.0   main-is:          Spec.hs   hs-source-dirs:   test   build-depends:-    , async-    , base        >=4.12 && <5-    , hspec-    , uuid+    , async       ^>=2.2+    , base        >=4.12  && <5+    , hspec       ^>=2.11+    , uuid        ^>=1.3     , Z-Data     , zoovisitor