proto-lens-protoc 0.3.1.0 → 0.3.1.1
raw patch · 3 files changed
+140/−104 lines, 3 filesdep +temporarydep ~basePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: temporary
Dependency ranges changed: base
API changes (from Hackage documentation)
- Data.ProtoLens.Compiler.Definitions: instance Data.Semigroup.Semigroup Data.ProtoLens.Compiler.Definitions.Symbol
+ Data.ProtoLens.Compiler.Definitions: instance GHC.Base.Semigroup Data.ProtoLens.Compiler.Definitions.Symbol
- Data.ProtoLens.Compiler.Combinators: data ImportDecl l :: * -> *
+ Data.ProtoLens.Compiler.Combinators: data ImportDecl l
Files
- Changelog.md +5/−0
- proto-lens-protoc.cabal +26/−26
- src/Data/ProtoLens/Setup.hs +109/−78
Changelog.md view
@@ -1,5 +1,10 @@ # Changelog for `proto-lens-protoc` +## v0.3.1.1+- Fix management of generated files between Cabal components (#171).+- Bump the lower bound on `base` to indicate we require `ghc>=8.0`.+- Use autogen-modules. (#196)+ ## v0.3.1.0 - Bump the dependency on `base` for `ghc-8.4.2`. - Bump the dependency to `Cabal-2.2.*`.
proto-lens-protoc.cabal view
@@ -1,11 +1,11 @@--- This file has been generated from package.yaml by hpack version 0.20.0.+-- This file has been generated from package.yaml by hpack version 0.28.2. -- -- see: https://github.com/sol/hpack ----- hash: b5e426f5c980c968df8a4126b27017a59590920178c0bb0902b138cf963d6282+-- hash: a69cd6e6704df4e3fa208607e7488ad6ff71860a2b10efeb7d46ab1e5e645c63 name: proto-lens-protoc-version: 0.3.1.0+version: 0.3.1.1 synopsis: Protocol buffer compiler for the proto-lens library. description: Turn protocol buffer files (.proto) into Haskell files (.hs) which can be used with the proto-lens package. The library component of this package contains compiler code (namely Data.ProtoLens.Compiler.*) is not guaranteed to have stable APIs.'@@ -18,8 +18,7 @@ license: BSD3 license-file: LICENSE build-type: Simple-cabal-version: >= 1.21-+cabal-version: >= 1.22 extra-source-files: Changelog.md @@ -34,24 +33,6 @@ default: False library- hs-source-dirs:- src- build-depends:- Cabal >=1.22 && <2.3- , base >=4.8 && <4.12- , bytestring ==0.10.*- , containers ==0.5.*- , data-default-class >=0.0 && <0.2- , deepseq ==1.4.*- , directory >=1.2 && <1.4- , filepath >=1.4 && <1.6- , haskell-src-exts >=1.17 && <1.21- , lens-family ==1.2.*- , lens-labels ==0.2.*- , pretty ==1.1.*- , process >=1.2 && <1.7- , proto-lens ==0.3.*- , text ==1.2.* exposed-modules: Data.ProtoLens.Compiler.Combinators Data.ProtoLens.Compiler.Definitions@@ -77,14 +58,35 @@ Lens.Labels as Data.ProtoLens.Reexport.Lens.Labels, Lens.Labels.Prism as Data.ProtoLens.Reexport.Lens.Labels.Prism, Text.Read as Data.ProtoLens.Reexport.Text.Read+ hs-source-dirs:+ src+ build-depends:+ Cabal >=1.22 && <2.3+ , base >=4.9 && <4.12+ , bytestring ==0.10.*+ , containers ==0.5.*+ , data-default-class >=0.0 && <0.2+ , deepseq ==1.4.*+ , directory >=1.2 && <1.4+ , filepath >=1.4 && <1.6+ , haskell-src-exts >=1.17 && <1.21+ , lens-family ==1.2.*+ , lens-labels ==0.2.*+ , pretty ==1.1.*+ , process >=1.2 && <1.7+ , proto-lens ==0.3.*+ , temporary ==1.2.*+ , text ==1.2.* default-language: Haskell2010 executable proto-lens-protoc main-is: protoc-gen-haskell.hs+ other-modules:+ Paths_proto_lens_protoc hs-source-dirs: app build-depends:- base >=4.8 && <4.12+ base >=4.9 && <4.12 , bytestring ==0.10.* , containers ==0.5.* , data-default-class >=0.0 && <0.2@@ -95,6 +97,4 @@ , proto-lens ==0.3.* , proto-lens-protoc , text ==1.2.*- other-modules:- Paths_proto_lens_protoc default-language: Haskell2010
src/Data/ProtoLens/Setup.hs view
@@ -26,12 +26,11 @@ import Control.DeepSeq (force) import Control.Monad (filterM, forM_, guard, when)-#if MIN_VERSION_Cabal(2,0,0)-import qualified Data.Map as Map-#endif import qualified Data.ByteString as BS+import qualified Data.Map as Map import Data.Maybe (maybeToList) import qualified Data.Set as Set+import qualified Data.Text as T import Distribution.ModuleName (ModuleName) import qualified Distribution.ModuleName as ModuleName import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo@@ -56,6 +55,7 @@ ( LocalBuildInfo(..) , absoluteInstallDirs , ComponentName(..)+ , ComponentLocalBuildInfo , componentPackageDeps #if MIN_VERSION_Cabal(2,0,0) , allComponentsInBuildOrder@@ -78,6 +78,7 @@ import Distribution.Verbosity (Verbosity) import System.FilePath ( (</>)+ , (<.>) , equalFilePath , isRelative , makeRelative@@ -85,14 +86,15 @@ , takeExtension ) import System.Directory- ( createDirectoryIfMissing+ ( copyFile+ , createDirectoryIfMissing , doesDirectoryExist , doesFileExist , findExecutable , removeDirectoryRecursive- , renameFile ) import System.IO (hPutStrLn, stderr)+import System.IO.Temp (withSystemTempDirectory) import System.Process (callProcess) import qualified Data.ProtoLens.Compiler.Plugin as Plugin@@ -158,15 +160,11 @@ getProtos l = do -- Replicate Cabal's own logic for parsing file globs. files <- concat <$> mapM matchFileGlob (extraSrcFiles $ localPkgDescr l)- let activeModules = Set.fromList $ collectActiveModules l- pure . filter (\f -> relativeFileToProtoModule f- `Set.member` activeModules)+ pure . filter (\f -> takeExtension f == ".proto") . map (makeRelative root) . filter (isSubdirectoryOf root) $ files- relativeFileToProtoModule- = ModuleName.fromString . Plugin.moduleNameStr "Proto" -- | Augment the given 'UserHooks' to auto-generate Haskell files from the@@ -187,11 +185,16 @@ { buildHook = \p l h f -> generate l >> buildHook hooks p l h f , haddockHook = \p l h f -> generate l >> haddockHook hooks p l h f , replHook = \p l h f args -> generate l >> replHook hooks p l h f args+#if !MIN_VERSION_Cabal(2,0,0)+ -- Older versions of Cabal don't support the autogen-modules field.+ -- Work around it by manually generating the modules and putting them+ -- in a place where `cabal sdist` will pick them up. , sDistHook = \p maybe_l h f -> case maybe_l of Nothing -> error "Can't run protoc; run 'cabal configure' first." Just l -> do generate l sDistHook hooks (fudgePackageDesc l p) maybe_l h f+#endif , postCopy = \a flags pkg lbi -> do let verb = fromFlag $ copyVerbosity flags let destDir = datadir (absoluteInstallDirs pkg lbi@@ -204,52 +207,70 @@ generate l = getProtos l >>= generateSources root l -- | Generate Haskell source files for the given input .proto files.+--+-- Process all the proto files that are referenced in the exposed-modules+-- or other-modules of some "active" component, and write them all to a+-- single temporary directory. (For example, passing --no-enable-tests+-- makes all test-suite components inactive.)+--+-- Then, for each active component, copy the corresponding module files+-- over to its specific autogen directory (if Cabal-2.*) or to the global+-- autogen directory (if Cabal-1.*). However, don't actually do the copy+-- if it's the same as what's already there. This way, we don't needlessly+-- touch the generated .hs files when nothing changes, and thus don't+-- needlessly make GHC recompile them (as it considers their modification+-- times for that). generateSources :: FilePath -- ^ The root directory -> LocalBuildInfo -> [FilePath] -- ^ Proto files relative to the root directory. -> IO ()-generateSources _ _ [] = return ()-generateSources root l files = do+generateSources root l files = withSystemTempDirectory "protoc-out" $ \tmpDir -> do -- Collect import paths from build-depends of this package. importDirs <- filterM doesDirectoryExist [ InstalledPackageInfo.dataDir info </> protoLensImportsPrefix | info <- collectDeps l ]- -- Generate .hs files into a temporary directory, then move them over- -- to the target (autogen) directory only if they are different from- -- what's already there. This way, we don't needlessly touch the generated- -- .hs files when nothing changes, and thus don't needlessly make GHC- -- recompile them (as it considers their modification times for that).- let tmpAutogenModulesDir = autogenModulesDir l ++ "-protoc-tmpOutDir"- -- Generate .hs files into temp dir.- generateProtosWithImports (root : importDirs) tmpAutogenModulesDir+ -- Generate .hs files for all active components into a single temporary+ -- directory.+ let activeModules = collectActiveModules l+ let allModules = Set.fromList . concat . map snd $ activeModules+ let usedInComponent f = ModuleName.fromString (Plugin.moduleNameStr "Proto" f)+ `Set.member` allModules+ generateProtosWithImports (root : importDirs) tmpDir -- Applying 'root </>' does nothing if the path is already -- absolute.- (map (root </>) files)- -- Discover generated files.- -- `getDirectoryContentsRecursive` is lazy IO, so we `force` through- -- the list to make it strict hereinafter.- !generatedFiles <- force <$> getDirectoryContentsRecursive tmpAutogenModulesDir- -- Move files to autogen dir only if file contents are different.- forM_ generatedFiles $ \pathRelativeToTmpDir -> do- let sourcePath = tmpAutogenModulesDir </> pathRelativeToTmpDir- let targetPath = autogenModulesDir l </> pathRelativeToTmpDir- identical <- do- targetExists <- doesFileExist targetPath- if not targetExists- then return False- else do- -- This could be done in a streaming fashion,- -- but since the .hs files usually easily fit- -- into RAM, this is OK.- sourceContents <- BS.readFile sourcePath- targetContents <- BS.readFile targetPath- return (sourceContents == targetContents)- -- Do the move if necessary.- when (not identical) $ do- createDirectoryIfMissing True (takeDirectory targetPath)- renameFile sourcePath targetPath+ $ map (root </>) $ filter usedInComponent files+ -- Copy each active component's files over to its autogen directory, but+ -- only if they've changed since last time.+ forM_ activeModules $ \(compBI, mods) -> forM_ mods $ \m -> do+ let f = T.unpack (Plugin.outputFilePath $ ModuleName.toFilePath m)+ let sourcePath = tmpDir </> f+ sourceExists <- doesFileExist sourcePath+ when sourceExists $ do+ let dest = autogenComponentModulesDir l compBI </> f+ copyIfDifferent sourcePath dest +-- Note: we do a copy rather than a move since a given module may be used in+-- more than one component.+copyIfDifferent :: FilePath -> FilePath -> IO ()+copyIfDifferent sourcePath targetPath = do+ targetExists <- doesFileExist targetPath+ identical <- do+ if not targetExists+ then return False+ else do+ -- This could be done in a streaming fashion,+ -- but since the .hs files usually easily fit+ -- into RAM, this is OK.+ sourceContents <- BS.readFile sourcePath+ targetContents <- BS.readFile targetPath+ return (sourceContents == targetContents)+ -- Do the move if necessary.+ when (not identical) $ do+ createDirectoryIfMissing True (takeDirectory targetPath)+ copyFile sourcePath targetPath++ -- | Copy each .proto file into the installed "data-dir" path, -- so that it can be included by other packages that depend on this one. copyProtosToDataDir :: Verbosity@@ -281,22 +302,30 @@ fudgePackageDesc :: LocalBuildInfo -> PackageDescription -> PackageDescription fudgePackageDesc lbi p = p { library =- (\lib -> lib { libBuildInfo = fudgeBuildInfo (libBuildInfo lib) })+ (\lib -> lib { libBuildInfo = fudgeBuildInfo CLibName $ libBuildInfo lib }) <$> library p , executables =- (\exe -> exe { buildInfo = fudgeBuildInfo (buildInfo exe) })+ (\exe -> exe { buildInfo = fudgeBuildInfo (CExeName $ exeName exe)+ $ buildInfo exe }) <$> executables p , testSuites =- (\test -> test { testBuildInfo = fudgeBuildInfo (testBuildInfo test) })+ (\test -> test { testBuildInfo = fudgeBuildInfo (CTestName $ testName test)+ $ testBuildInfo test }) <$> testSuites p , benchmarks = (\bench -> bench { benchmarkBuildInfo =- fudgeBuildInfo (benchmarkBuildInfo bench) })+ fudgeBuildInfo (CBenchName $ benchmarkName bench)+ $ benchmarkBuildInfo bench }) <$> benchmarks p } where- fudgeBuildInfo bi =- bi { hsSourceDirs = autogenModulesDir lbi : hsSourceDirs bi }+ comps = allComponents lbi+ fudgeBuildInfo n bi+ | Just compLBI <- Map.lookup n comps+ = bi { hsSourceDirs = autogenComponentModulesDir lbi compLBI+ : hsSourceDirs bi }+ | otherwise = bi -- Could happen if a component isn't active; try+ -- anyway and see whether Cabal complains later on. -- | Returns whether the @root@ is a parent folder of @f@. isSubdirectoryOf :: FilePath -> FilePath -> Bool@@ -331,6 +360,7 @@ -> FilePath -- ^ The output directory for the generated Haskell files. -> [FilePath] -- ^ The .proto files to process. -> IO ()+generateProtosWithImports _ _ [] = return () generateProtosWithImports imports output files = do protoLensProtoc <- findExecutableOrDie "proto-lens-protoc"@@ -366,25 +396,22 @@ -- | Collect all the module names that we need to build. -- For example: only include test-suites if we're building with tests enabled -- (e.g., `stack test` vs `stack build`).-collectActiveModules :: LocalBuildInfo -> [ModuleName]-collectActiveModules l = let- in (activeLib >>= exposedModules)- ++ concatMap otherModules- (concat- [ libBuildInfo <$> activeLib- , buildInfo <$> activeExes- , testBuildInfo <$> activeTests- , benchmarkBuildInfo <$> activeBenchmarks- ])+collectActiveModules+ :: LocalBuildInfo -> [(ComponentLocalBuildInfo, [ModuleName])]+collectActiveModules l = map (\(n, l) -> (l, f n)) $ Map.toList $ allComponents l where p = localPkgDescr l- activeLib = guard (active CLibName) >> maybeToList (library p)- activeExes = filter (active . CExeName . exeName) $ executables p- activeTests = filter (active . CTestName . testName) $ testSuites p- activeBenchmarks = filter (active . CBenchName . benchmarkName)- $ benchmarks p- comps = Set.fromList $ allComponentNames l- active = (`Set.member` comps)+ f CLibName = maybeToList (library p) >>= \l -> exposedModules l+ ++ otherModules (libBuildInfo l)+ f (CExeName n) = otherModules . buildInfo $ exes Map.! n+ f (CTestName n) = otherModules . testBuildInfo $ tests Map.! n+ f (CBenchName n) = otherModules . benchmarkBuildInfo $ benchs Map.! n+#if MIN_VERSION_Cabal(2,0,0)+ f _ = [] -- TODO: other lib kinds; for now just suppress the warning+#endif+ exes = Map.fromList [(exeName e, e) | e <- executables p]+ tests = Map.fromList [(testName e, e) | e <- testSuites p]+ benchs = Map.fromList [(benchmarkName e, e) | e <- benchmarks p] ------------------------------------------------------- -- Compatibility layer between Cabal-1.* and Cabal-2.*@@ -405,21 +432,25 @@ #endif -- | All the components that will be built by this Cabal command.-allComponentNames :: LocalBuildInfo -> [ComponentName]+allComponents :: LocalBuildInfo -> Map.Map ComponentName ComponentLocalBuildInfo #if MIN_VERSION_Cabal(2,0,0)-allComponentNames l = Map.keys $ componentNameMap l+allComponents l = fmap requireOne $ componentNameMap l+ where+ -- TODO: this doesn't support Backpack, which can have more than one+ -- ComponentLocalBuildInfo associated with a name.+ requireOne [x] = x+ requireOne xs = error $ "Data.ProtoLens.Setup.allComponents: expected one "+ ++ "component per name, got " ++ show xs+ #else-allComponentNames l = [c | (c, _, _) <- componentsConfigs l]+allComponents l = Map.fromList [(c, b) | (c, b, _) <- componentsConfigs l] #endif --- | Get the package-level "autogen" directory where we're putting the--- generated .hs files. (The 'BuildPaths.autogenModulesDir' file was--- deprecated in Cabal-2.0 in favor of module-specific directories--- (@autogenComponentModulesDir@), but our setup currently needs the more--- global location.)-autogenModulesDir :: LocalBuildInfo -> FilePath+-- | Get the component-level "autogen" directory where we're putting the+-- generated .hs files. (For Cabal-1.0, use the shared 'BuildPaths.autogenModulesDir'.)+autogenComponentModulesDir :: LocalBuildInfo -> ComponentLocalBuildInfo -> FilePath #if MIN_VERSION_Cabal(2,0,0)-autogenModulesDir = BuildPaths.autogenPackageModulesDir+autogenComponentModulesDir = BuildPaths.autogenComponentModulesDir #else-autogenModulesDir = BuildPaths.autogenModulesDir+autogenComponentModulesDir lbi _ = BuildPaths.autogenModulesDir lbi #endif