From 3e2ccc0842ce57131939cab7eda32a8c9b5a1251 Mon Sep 17 00:00:00 2001 From: Moritz Kiefer Date: Mon, 19 Aug 2019 20:15:13 +0200 Subject: [PATCH] Switch to a streaming zip encoding (#2595) This switches the creation of the archive in `daml build` from `zip-archive` to `zip`. This has a few advantages: 1. It gets rid of lazy IO for reading all the interface and source files. This avoids the high usage of file handles in `daml build`. 2. It seems to be a slight improvement in max memory usage and runtime and a giant improvement in allocations (but I think the latter probably comes primarily from the fact that the locations are moved to the bzip C library). The improvement in max memory usage is less than I expected so probably there is still something off somewhere. For now, I only switched over `createArchive`. Archive reading is still done using `zip-archive`. We might want to switch that over in a separate PR. --- 3rdparty/c/bzip2.BUILD | 24 ++++++ 3rdparty/haskell/BUILD.bindings-DSL | 18 +++++ 3rdparty/haskell/bzlib-conduit.patch | 13 ++++ WORKSPACE | 17 +++++ .../daml-lf-reader/src/DA/Daml/LF/Reader.hs | 2 +- compiler/damlc/BUILD.bazel | 1 + compiler/damlc/daml-compiler/BUILD.bazel | 3 +- .../daml-compiler/src/DA/Daml/Compiler/Dar.hs | 51 ++++++------- compiler/damlc/lib/DA/Cli/Damlc.hs | 75 ++++++++++--------- compiler/damlc/lib/DA/Cli/Visual.hs | 2 +- deps.bzl | 9 +++ unreleased.rst | 2 + 12 files changed, 147 insertions(+), 70 deletions(-) create mode 100644 3rdparty/c/bzip2.BUILD create mode 100644 3rdparty/haskell/BUILD.bindings-DSL create mode 100644 3rdparty/haskell/bzlib-conduit.patch diff --git a/3rdparty/c/bzip2.BUILD b/3rdparty/c/bzip2.BUILD new file mode 100644 index 000000000000..056fafaa68c4 --- /dev/null +++ b/3rdparty/c/bzip2.BUILD @@ -0,0 +1,24 @@ +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "bz2", + srcs = [ + "blocksort.c", + "bzlib.c", + "compress.c", + "crctable.c", + "decompress.c", + "huffman.c", + "randtable.c", + ], + hdrs = [ + "bzlib.h", + "bzlib_private.h", + ], + includes = [ + ".", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/3rdparty/haskell/BUILD.bindings-DSL b/3rdparty/haskell/BUILD.bindings-DSL new file mode 100644 index 000000000000..fd534cbd4570 --- /dev/null +++ b/3rdparty/haskell/BUILD.bindings-DSL @@ -0,0 +1,18 @@ +package(default_visibility = ["//visibility:public"]) + +load("@rules_haskell//haskell:defs.bzl", "haskell_library") +load("@ai_formation_hazel//:hazel.bzl", "hazel_library") + +haskell_library( + name = "lib", + visibility = ["//visibility:public"], + srcs = [":Bindings/Utilities.hs"], + deps = [hazel_library("base"), ":bindings-DSL-cbits"], +) + + +cc_library( + name = "bindings-DSL-cbits", + visibility = ["//visibility:public"], + hdrs = [":bindings.dsl.h", ":bindings.cmacros.h"], +) diff --git a/3rdparty/haskell/bzlib-conduit.patch b/3rdparty/haskell/bzlib-conduit.patch new file mode 100644 index 000000000000..26f168ec62aa --- /dev/null +++ b/3rdparty/haskell/bzlib-conduit.patch @@ -0,0 +1,13 @@ +diff --git a/src/Data/Conduit/BZlib/Internal.hsc b/src/Data/Conduit/BZlib/Internal.hsc +index f980d35..18d6e3a 100644 +--- a/src/Data/Conduit/BZlib/Internal.hsc ++++ b/src/Data/Conduit/BZlib/Internal.hsc +@@ -1,7 +1,7 @@ + {-# LANGUAGE ForeignFunctionInterface #-} + + #include +-#include ++#include "bindings.dsl.h" + + module Data.Conduit.BZlib.Internal where + diff --git a/WORKSPACE b/WORKSPACE index da0676fd6bd0..e810819f394b 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -494,6 +494,7 @@ hazel_repositories( }, ), exclude_packages = [ + "bindings-DSL", "clock", # Excluded since we build it via the http_archive line above. "ghc-lib-parser", @@ -515,6 +516,7 @@ hazel_repositories( { "z": "@com_github_madler_zlib//:z", "ffi": "" if is_windows else "@libffi_nix//:ffi", + "bz2": "@bzip2//:bz2", }, ), ghc_workspaces = { @@ -586,6 +588,12 @@ hazel_repositories( "91dd121ac565009f2fc215c50f3365ed66705071a698a545e869041b5d7ff4da", patch_args = ["-p1"], patches = ["@com_github_digital_asset_daml//bazel_tools:haskell-c2hs.patch"], + ) + hazel_hackage( + "bzlib-conduit", + "0.3.0.2", + "eb2c732b3d4ab5f7b367c51eef845e597ade19da52c03ee11954d35b6cfc4128", + patch_args = ["-p1"], + patches = ["@com_github_digital_asset_daml//3rdparty/haskell:bzlib-conduit.patch"], ), pkgs = packages, ), @@ -611,6 +619,15 @@ hazel_custom_package_hackage( version = "0.6.2", ) +hazel_custom_package_hackage( + package_name = "bindings-DSL", + # Without a custom build file, packages depending on bindings-DSL + # fail to find bindings.dsl.h. + build_file = "//3rdparty/haskell:BUILD.bindings-DSL", + sha256 = "63de32380c68d1cc5e9c7b3622d67832c786da21163ba0c8a4835e6dd169194f", + version = "1.0.25", +) + hazel_custom_package_hackage( package_name = "streaming-commons", build_file = "//3rdparty/haskell:BUILD.streaming-commons", diff --git a/compiler/daml-lf-reader/src/DA/Daml/LF/Reader.hs b/compiler/daml-lf-reader/src/DA/Daml/LF/Reader.hs index 1185d8ca45bd..5ad9df663df0 100644 --- a/compiler/daml-lf-reader/src/DA/Daml/LF/Reader.hs +++ b/compiler/daml-lf-reader/src/DA/Daml/LF/Reader.hs @@ -9,7 +9,7 @@ module DA.Daml.LF.Reader , getManifestField ) where -import Codec.Archive.Zip +import "zip-archive" Codec.Archive.Zip import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString.Lazy.UTF8 as UTF8 import qualified Data.ByteString.Char8 as BSC diff --git a/compiler/damlc/BUILD.bazel b/compiler/damlc/BUILD.bazel index f6e7aee04897..5e23040aa495 100644 --- a/compiler/damlc/BUILD.bazel +++ b/compiler/damlc/BUILD.bazel @@ -120,6 +120,7 @@ da_haskell_library( "vector", "xml", "yaml", + "zip", "zip-archive", "unordered-containers", "uniplate", diff --git a/compiler/damlc/daml-compiler/BUILD.bazel b/compiler/damlc/daml-compiler/BUILD.bazel index 31ee7f7e20d0..f3609fe08f33 100644 --- a/compiler/damlc/daml-compiler/BUILD.bazel +++ b/compiler/damlc/daml-compiler/BUILD.bazel @@ -12,6 +12,7 @@ da_haskell_library( hazel_deps = [ "base", "bytestring", + "conduit", "containers", "directory", "extra", @@ -27,7 +28,7 @@ da_haskell_library( "text", "time", "transformers", - "zip-archive", + "zip", ], src_strip_prefix = "src", visibility = ["//visibility:public"], diff --git a/compiler/damlc/daml-compiler/src/DA/Daml/Compiler/Dar.hs b/compiler/damlc/daml-compiler/src/DA/Daml/Compiler/Dar.hs index 7a231af16f60..9d916f6abfe3 100644 --- a/compiler/damlc/daml-compiler/src/DA/Daml/Compiler/Dar.hs +++ b/compiler/damlc/daml-compiler/src/DA/Daml/Compiler/Dar.hs @@ -8,7 +8,7 @@ module DA.Daml.Compiler.Dar , pkgNameVersion ) where -import qualified Codec.Archive.Zip as Zip +import qualified "zip" Codec.Archive.Zip as Zip import Control.Monad.Extra import Control.Monad.IO.Class import Control.Monad.Trans.Maybe @@ -18,6 +18,7 @@ import DA.Daml.Options.Types import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString.Lazy.Char8 as BSC +import Data.Conduit.Combinators (sourceFile, sourceLazy) import Data.List.Extra import qualified Data.Map.Strict as Map import Data.Maybe @@ -78,16 +79,15 @@ buildDar :: -> PackageConfigFields -> NormalizedFilePath -> FromDalf - -> IO (Maybe BSL.ByteString) + -> IO (Maybe (Zip.ZipArchive ())) buildDar service pkgConf@PackageConfigFields {..} ifDir dalfInput = do liftIO $ IdeLogger.logDebug (ideLogger service) $ "Creating dar: " <> T.pack pMain if unFromDalf dalfInput - then liftIO $ - Just <$> do - bytes <- BSL.readFile pMain - createArchive pkgConf "" bytes [] (toNormalizedFilePath ".") [] [] [] + then do + bytes <- BSL.readFile pMain + pure $ Just $ createArchive pkgConf "" bytes [] (toNormalizedFilePath ".") [] [] [] else runAction service $ runMaybeT $ do WhnfPackage pkg <- useE GeneratePackage file @@ -118,7 +118,7 @@ buildDar service pkgConf@PackageConfigFields {..} ifDir dalfInput = do fileDependencies <- MaybeT $ getDependencies file let dataFiles = [mkConfFile pkgConf pkgModuleNames (T.unpack pkgId)] - liftIO $ + pure $ createArchive pkgConf (T.unpack pkgId) @@ -176,29 +176,21 @@ createArchive :: -> [NormalizedFilePath] -- ^ Module dependencies -> [(String, BS.ByteString)] -- ^ Data files -> [NormalizedFilePath] -- ^ Interface files - -> IO BSL.ByteString + -> Zip.ZipArchive () createArchive PackageConfigFields {..} pkgId dalf dalfDependencies srcRoot fileDependencies dataFiles ifaces = do -- Reads all module source files, and pairs paths (with changed prefix) -- with contents as BS. The path must be within the module root path, and -- is modified to have prefix instead of the original root path. - srcFiles <- - forM fileDependencies $ \mPath -> do - contents <- BSL.readFile $ fromNormalizedFilePath mPath - return - ( pkgName - fromNormalizedFilePath (makeRelative' srcRoot mPath) - , contents) - ifaceFaceFiles <- - forM ifaces $ \mPath -> do - contents <- BSL.readFile $ fromNormalizedFilePath mPath - let ifaceRoot = - toNormalizedFilePath - (ifaceDir fromNormalizedFilePath srcRoot) - return - ( pkgName - fromNormalizedFilePath (makeRelative' ifaceRoot mPath) - , contents) + forM_ fileDependencies $ \mPath -> do + entry <- Zip.mkEntrySelector $ pkgName fromNormalizedFilePath (makeRelative' srcRoot mPath) + Zip.sinkEntry Zip.Deflate (sourceFile $ fromNormalizedFilePath mPath) entry + forM_ ifaces $ \mPath -> do + let ifaceRoot = + toNormalizedFilePath + (ifaceDir fromNormalizedFilePath srcRoot) + entry <- Zip.mkEntrySelector $ pkgName fromNormalizedFilePath (makeRelative' ifaceRoot mPath) + Zip.sinkEntry Zip.Deflate (sourceFile $ fromNormalizedFilePath mPath) entry let dalfName = pkgName pkgNameVersion pName pVersion <.> "dalf" let dependencies = [ (pkgName T.unpack depName <> ".dalf", BSL.fromStrict bs) @@ -213,11 +205,10 @@ createArchive PackageConfigFields {..} pkgId dalf dalfDependencies srcRoot fileD ( "META-INF/MANIFEST.MF" , manifestHeader dalfName (dalfName : map fst dependencies)) : (dalfName, dalf) : - srcFiles ++ ifaceFaceFiles ++ dependencies ++ dataFiles' - mkEntry (filePath, content) = Zip.toEntry filePath 0 content - zipArchive = - foldr (Zip.addEntryToArchive . mkEntry) Zip.emptyArchive allFiles - pure $ Zip.fromArchive zipArchive + dependencies ++ dataFiles' + forM_ allFiles $ \(file, content) -> do + entry <- Zip.mkEntrySelector file + Zip.sinkEntry Zip.Deflate (sourceLazy content) entry where pkgName = fullPkgName pName pVersion pkgId manifestHeader :: FilePath -> [String] -> BSL.ByteString diff --git a/compiler/damlc/lib/DA/Cli/Damlc.hs b/compiler/damlc/lib/DA/Cli/Damlc.hs index d18b0c23d8a7..739e7ebe70fa 100644 --- a/compiler/damlc/lib/DA/Cli/Damlc.hs +++ b/compiler/damlc/lib/DA/Cli/Damlc.hs @@ -7,7 +7,8 @@ -- | Main entry-point of the DAML compiler module DA.Cli.Damlc (main) where -import Codec.Archive.Zip +import qualified "zip-archive" Codec.Archive.Zip as ZipArchive +import qualified "zip" Codec.Archive.Zip as Zip import Control.Exception import Control.Exception.Safe (catchIO) import Control.Monad.Except @@ -415,37 +416,37 @@ createProjectPackageDb (AllowDifferentSdkVersions allowDiffSdkVersions) lfVersio sdkVersions <- forM fps0 $ \fp -> do bs <- BSL.readFile fp - let archive = toArchive bs + let archive = ZipArchive.toArchive bs manifest <- getEntry "META-INF/MANIFEST.MF" archive sdkVersion <- trim <$> getManifestFieldOrErr manifest "Sdk-Version" let confFiles = [ e - | e <- zEntries archive - , ".conf" `isExtensionOf` eRelativePath e + | e <- ZipArchive.zEntries archive + , ".conf" `isExtensionOf` ZipArchive.eRelativePath e ] let dalfs = [ e - | e <- zEntries archive - , ".dalf" `isExtensionOf` eRelativePath e + | e <- ZipArchive.zEntries archive + , ".dalf" `isExtensionOf` ZipArchive.eRelativePath e ] let srcs = [ e - | e <- zEntries archive - , takeExtension (eRelativePath e) `elem` + | e <- ZipArchive.zEntries archive + , takeExtension (ZipArchive.eRelativePath e) `elem` [".daml", ".hie", ".hi"] ] forM_ dalfs $ \dalf -> do - let path = dbPath eRelativePath dalf + let path = dbPath ZipArchive.eRelativePath dalf createDirectoryIfMissing True (takeDirectory path) - BSL.writeFile path (fromEntry dalf) + BSL.writeFile path (ZipArchive.fromEntry dalf) forM_ confFiles $ \conf -> BSL.writeFile (dbPath "package.conf.d" - (takeFileName $ eRelativePath conf)) - (fromEntry conf) + (takeFileName $ ZipArchive.eRelativePath conf)) + (ZipArchive.fromEntry conf) forM_ srcs $ \src -> do - let path = dbPath eRelativePath src - write path (fromEntry src) + let path = dbPath ZipArchive.eRelativePath src + write path (ZipArchive.fromEntry src) pure sdkVersion let uniqSdkVersions = nubSort sdkVersions -- if there are no package dependencies, sdkVersions will be empty @@ -491,7 +492,7 @@ execBuild projectOpts options mbOutFile initPkgDb = withProjectRoot' projectOpts dar <- mbErr "ERROR: Creation of DAR file failed." mbDar let fp = targetFilePath $ pkgNameVersion pName pVersion createDirectoryIfMissing True $ takeDirectory fp - BSL.writeFile fp dar + Zip.createArchive fp dar putStrLn $ "Created " <> fp <> "." where targetFilePath name = fromMaybe (distDir name <.> "dar") mbOutFile @@ -546,7 +547,7 @@ execPackage projectOpts filePath opts mbOutFile dalfInput = withProjectRoot' pro exitFailure Just dar -> do createDirectoryIfMissing True $ takeDirectory targetFilePath - BSL.writeFile targetFilePath dar + Zip.createArchive targetFilePath dar putStrLn $ "Created " <> targetFilePath <> "." where -- This is somewhat ugly but our CLI parser guarantees that this will always be present. @@ -567,7 +568,7 @@ execPackage projectOpts filePath opts mbOutFile dalfInput = withProjectRoot' pro execInspect :: FilePath -> FilePath -> Bool -> DA.Pretty.PrettyLevel -> Command execInspect inFile outFile jsonOutput lvl = do let mainDalf - | "dar" `isExtensionOf` inFile = BSL.toStrict . mainDalfContent . manifestFromDar . toArchive . BSL.fromStrict + | "dar" `isExtensionOf` inFile = BSL.toStrict . mainDalfContent . manifestFromDar . ZipArchive.toArchive . BSL.fromStrict | otherwise = id bytes <- mainDalf <$> B.readFile inFile @@ -596,21 +597,21 @@ execInspectDar inFile = do bytes <- B.readFile inFile putStrLn "DAR archive contains the following files: \n" - let dar = toArchive $ BSL.fromStrict bytes - let files = [eRelativePath e | e <- zEntries dar] + let dar = ZipArchive.toArchive $ BSL.fromStrict bytes + let files = [ZipArchive.eRelativePath e | e <- ZipArchive.zEntries dar] mapM_ putStrLn files putStrLn "\nDAR archive contains the following packages: \n" let dalfEntries = - [e | e <- zEntries dar, ".dalf" `isExtensionOf` eRelativePath e] + [e | e <- ZipArchive.zEntries dar, ".dalf" `isExtensionOf` ZipArchive.eRelativePath e] forM_ dalfEntries $ \dalfEntry -> do - let dalf = BSL.toStrict $ fromEntry dalfEntry + let dalf = BSL.toStrict $ ZipArchive.fromEntry dalfEntry (pkgId, _lfPkg) <- errorOnLeft - ("Cannot decode package " <> eRelativePath dalfEntry) + ("Cannot decode package " <> ZipArchive.eRelativePath dalfEntry) (Archive.decodeArchive dalf) putStrLn $ - (dropExtension $ takeFileName $ eRelativePath dalfEntry) <> " " <> + (dropExtension $ takeFileName $ ZipArchive.eRelativePath dalfEntry) <> " " <> show (LF.unPackageId pkgId) execMigrate :: @@ -694,12 +695,12 @@ execMigrate projectOpts opts0 inFile1_ inFile2_ mbDir = do forM [inFile1, inFile2] $ \inFile -> do bytes <- B.readFile inFile let pkgName = takeBaseName inFile - let dar = toArchive $ BSL.fromStrict bytes + let dar = ZipArchive.toArchive $ BSL.fromStrict bytes -- get the main pkg manifest <- getEntry "META-INF/MANIFEST.MF" dar mainDalfPath <- getManifestFieldOrErr manifest "Main-Dalf" mainDalfEntry <- getEntry mainDalfPath dar - (mainPkgId, mainLfPkg) <- decode $ BSL.toStrict $ fromEntry mainDalfEntry + (mainPkgId, mainLfPkg) <- decode $ BSL.toStrict $ ZipArchive.fromEntry mainDalfEntry pure (pkgName, mainPkgId, mainLfPkg) -- generate upgrade modules and instances modules let eqModNames = @@ -751,13 +752,13 @@ execMigrate projectOpts opts0 inFile1_ inFile2_ mbDir = do NM.lookup modName $ LF.packageModules pkg -- | Get an entry from a dar or fail. -getEntry :: FilePath -> Archive -> IO Entry +getEntry :: FilePath -> ZipArchive.Archive -> IO ZipArchive.Entry getEntry fp dar = maybe (fail $ "Package does not contain " <> fp) pure $ - findEntryByPath fp dar + ZipArchive.findEntryByPath fp dar -- | Parse a manifest field. -getManifestFieldOrErr :: Entry -> String -> IO String +getManifestFieldOrErr :: ZipArchive.Entry -> String -> IO String getManifestFieldOrErr manifest field = mbErr ("Missing field in META-INF/MANIFEST.MD: " ++ field) $ getManifestField manifest field @@ -769,29 +770,29 @@ execMergeDars darFp1 darFp2 mbOutFp = do let outFp = fromMaybe darFp1 mbOutFp bytes1 <- B.readFile darFp1 bytes2 <- B.readFile darFp2 - let dar1 = toArchive $ BSL.fromStrict bytes1 - let dar2 = toArchive $ BSL.fromStrict bytes2 + let dar1 = ZipArchive.toArchive $ BSL.fromStrict bytes1 + let dar2 = ZipArchive.toArchive $ BSL.fromStrict bytes2 mf <- mergeManifests dar1 dar2 let merged = - Archive - (nubSortOn eRelativePath $ mf : zEntries dar1 ++ zEntries dar2) + ZipArchive.Archive + (nubSortOn ZipArchive.eRelativePath $ mf : ZipArchive.zEntries dar1 ++ ZipArchive.zEntries dar2) -- nubSortOn keeps the first occurence Nothing BSL.empty - BSL.writeFile outFp $ fromArchive merged + BSL.writeFile outFp $ ZipArchive.fromArchive merged where mergeManifests dar1 dar2 = do let mfPath = "META-INF/MANIFEST.MF" let dalfNames = nubSort [ takeFileName p - | e <- zEntries dar1 ++ zEntries dar2 - , let p = eRelativePath e + | e <- ZipArchive.zEntries dar1 ++ ZipArchive.zEntries dar2 + , let p = ZipArchive.eRelativePath e , ".dalf" `isExtensionOf` p ] m1 <- getEntry mfPath dar1 let m' = do - l <- lines $ BSC.unpack $ BSL.toStrict $ fromEntry m1 + l <- lines $ BSC.unpack $ BSL.toStrict $ ZipArchive.fromEntry m1 pure $ maybe l @@ -799,7 +800,7 @@ execMergeDars darFp1 darFp2 mbOutFp = do breakAt72Chars $ "Dalfs: " <> intercalate ", " dalfNames) (stripPrefix "Dalfs:" l) - pure $ toEntry mfPath 0 $ BSL.fromStrict $ BSC.pack $ unlines m' + pure $ ZipArchive.toEntry mfPath 0 $ BSL.fromStrict $ BSC.pack $ unlines m' execDocTest :: Options -> [FilePath] -> IO () execDocTest opts files = do diff --git a/compiler/damlc/lib/DA/Cli/Visual.hs b/compiler/damlc/lib/DA/Cli/Visual.hs index 6e51f52d2fdf..7ccec5ee4311 100644 --- a/compiler/damlc/lib/DA/Cli/Visual.hs +++ b/compiler/damlc/lib/DA/Cli/Visual.hs @@ -19,7 +19,7 @@ import qualified Data.NameMap as NM import qualified Data.Set as Set import qualified DA.Pretty as DAP import qualified DA.Daml.LF.Proto3.Archive as Archive -import qualified Codec.Archive.Zip as ZIPArchive +import qualified "zip-archive" Codec.Archive.Zip as ZIPArchive import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString as B import Data.Generics.Uniplate.Data diff --git a/deps.bzl b/deps.bzl index 53b03ed1aa19..fe6822cf6de9 100644 --- a/deps.bzl +++ b/deps.bzl @@ -79,6 +79,15 @@ def daml_deps(): sha256 = "6d4d6640ca3121620995ee255945161821218752b551a1a180f4215f7d124d45", ) + if "bzip2" not in native.existing_rules(): + http_archive( + name = "bzip2", + build_file = "@com_github_digital_asset_daml//3rdparty/c:bzip2.BUILD", + strip_prefix = "bzip2-1.0.8", + urls = ["https://sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz"], + sha256 = "ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269", + ) + if "io_bazel_rules_go" not in native.existing_rules(): http_archive( name = "io_bazel_rules_go", diff --git a/unreleased.rst b/unreleased.rst index f6f157025e2b..81d478fff82f 100644 --- a/unreleased.rst +++ b/unreleased.rst @@ -14,3 +14,5 @@ HEAD — ongoing + [DAML Compiler] The DAML compiler was accidentally compiled without optimizations on Windows. This has been fixed which should improve the performance of ``damlc`` and ``daml studio`` on Windows. ++ [DAML Compiler] ``damlc build`` should no longer leak file handles so + ``ulimit`` workarounds should no longer be necessary.