Hello, 2 patches to contribute to cabal. The first is a simple oversite I
noticed while implementing the second. Essentially, the call out to an external
command by system has every supplied string wrapped in ', but doesn't escape
this character. This is fixed in the first patch.

Thu Aug  4 20:06:11 EDT 2011  m...@mattcox.ca
  * Protect internal ' in quoted system commands
  
  The previous implementation did not escape ' in the input which causes them to
  break the quotation.

The second is support that I require for a project which needs to use hooks to
invoke cmake and make to build a legacy C component. I followed the existing
code conventions which make a few questionable choices. For one, the
progInvokeEnv field is [(String, String)] rather than Maybe [(String, String)]
like is used by e.g., runInteractiveProcess. The interpretation in the existing
code is that this is a supplemental list of environment variables rather than a
complete environment. There is no way to remove existing variable with this
representation other than assigning them empty values.

This is a different interpretation to that used by rawSystemExitWithEnv, which
uses it as the entire environment. I had the runInvokation functions merge the
supplied environment on top of the system environment rather than modify the
behaviour of the rawSystemExitWithEnv function. The rawSystemExit functions
were given variants accepting working directories rather than adding a Maybe
FilePath argument and changing the type signature.

I hope that this can go in for 1.12. In particular, I'd like to suggest a bump
to 1.11.3 so that I can depend on the version to ensure the behaviour
introduced in this patch is available.

Fri Aug  5 12:19:48 EDT 2011  m...@mattcox.ca
  * Finish implementing Distribution.Simple.Program.Run
  
  The environment and working directory settings in the ProgramInvocation record
  were not totally handled by the run invocation functions in the module. The
  selection of a working directory was implemented for all functions, and input
  set in the invocation is handled by getProgramInvocation output.
  
  The environment in the record is taken to be a list of supplemental 
environment
  settings to add or override the system environment variables, rather than as a
  complete environment. This is the behaviour implemented by the functions in
  Distribution.Simple.Program.Script.
  
  New functions taking an environment and working directory were added to the
  Distribution.Simple.Utils module.
  

New patches:

[Protect internal ' in quoted system commands
m...@mattcox.ca**20110805000611
 Ignore-this: 393b6661e5fc222bcdcbdfb290760430
 
 The previous implementation did not escape ' in the input which causes them to
 break the quotation.
] hunk ./cabal/Distribution/Simple/Utils.hs 475
         hPutStr inHandle inputStr
     hClose inHandle
 
-    let quote name = "'" ++ name ++ "'"
+    let quote name = "'" ++ concatMap quote_c name ++ "'"
+        -- Quote protect a single character
+        quote_c '\'' = "\\'"
+        quote_c c = [c]
         cmd = unwords (map quote (path:args))
            ++ " <" ++ quote inName
            ++ " >" ++ quote outName
[Finish implementing Distribution.Simple.Program.Run
m...@mattcox.ca**20110805161948
 Ignore-this: e91fdea2fdd145d222082b6e93fe6d6e
 
 The environment and working directory settings in the ProgramInvocation record
 were not totally handled by the run invocation functions in the module. The
 selection of a working directory was implemented for all functions, and input
 set in the invocation is handled by getProgramInvocation output.
 
 The environment in the record is taken to be a list of supplemental environment
 settings to add or override the system environment variables, rather than as a
 complete environment. This is the behaviour implemented by the functions in
 Distribution.Simple.Program.Script.
 
 New functions taking an environment and working directory were added to the
 Distribution.Simple.Utils module.
 
] {
hunk ./cabal/Distribution/Simple/Program/Run.hs 28
 import Distribution.Simple.Program.Types
          ( ConfiguredProgram(..), programPath )
 import Distribution.Simple.Utils
-         ( die, rawSystemExit, rawSystemStdInOut
-         , toUTF8, fromUTF8, normaliseLineEndings )
+         ( die
+         , toUTF8, fromUTF8, normaliseLineEndings
+         , rawSystemExitWithEnv
+         , rawSystemExitWithCwdAndEnv
+         , rawSystemStdInOutWithCwdAndEnv
+         )
 import Distribution.Verbosity
          ( Verbosity )
 
hunk ./cabal/Distribution/Simple/Program/Run.hs 39
 import Data.List
          ( foldl', unfoldr )
+import qualified Data.Map as M
+         ( fromList, toList, insert)
 import Control.Monad
          ( when )
 import System.Exit
hunk ./cabal/Distribution/Simple/Program/Run.hs 46
          ( ExitCode(..) )
 
+import System.Environment
+         ( getEnvironment )
+
 -- | Represents a specific invocation of a specific program.
 --
 -- This is used as an intermediate type between deciding how to call a program
hunk ./cabal/Distribution/Simple/Program/Run.hs 59
 data ProgramInvocation = ProgramInvocation {
        progInvokePath  :: FilePath,
        progInvokeArgs  :: [String],
-       progInvokeEnv   :: [(String, String)],
+       progInvokeEnv   :: [(String, String)], -- ^ Supplemental environment
        progInvokeCwd   :: Maybe FilePath,
        progInvokeInput :: Maybe String,
        progInvokeInputEncoding  :: IOEncoding,
hunk ./cabal/Distribution/Simple/Program/Run.hs 97
                   ++ programOverrideArgs prog
   }
 
+mergeEnvironment :: [(String,String)] -> IO [(String,String)]
+mergeEnvironment new = do
+    env <- getEnvironment
+    let sysvars = M.fromList env
+        merged = foldr (uncurry M.insert) sysvars new
+    return (M.toList merged) 
 
 runProgramInvocation :: Verbosity -> ProgramInvocation -> IO ()
 runProgramInvocation verbosity
hunk ./cabal/Distribution/Simple/Program/Run.hs 109
   ProgramInvocation {
     progInvokePath  = path,
     progInvokeArgs  = args,
-    progInvokeEnv   = [],
-    progInvokeCwd   = Nothing,
+    progInvokeEnv   = env,
+    progInvokeCwd   = mcwd,
     progInvokeInput = Nothing
hunk ./cabal/Distribution/Simple/Program/Run.hs 112
-  } =
-  rawSystemExit verbosity path args
+  } = mergeEnvironment env >>= \renv -> case mcwd of
+         Nothing -> rawSystemExitWithEnv verbosity path args renv
+         Just dir -> rawSystemExitWithCwdAndEnv verbosity path args dir renv
 
 runProgramInvocation verbosity
   ProgramInvocation {
hunk ./cabal/Distribution/Simple/Program/Run.hs 120
     progInvokePath  = path,
     progInvokeArgs  = args,
-    progInvokeEnv   = [],
-    progInvokeCwd   = Nothing,
+    progInvokeEnv   = env,
+    progInvokeCwd   = mcwd,
     progInvokeInput = Just inputStr,
     progInvokeInputEncoding = encoding
   } = do
hunk ./cabal/Distribution/Simple/Program/Run.hs 125
-    (_, errors, exitCode) <- rawSystemStdInOut verbosity
-                                    path args
-                                    (Just input) False
+    renv <- mergeEnvironment env
+    (_, errors, exitCode) <- rawSystemStdInOutWithCwdAndEnv verbosity path args
+                              mcwd (Just renv) (Just input) False
     when (exitCode /= ExitSuccess) $
       die errors
   where
hunk ./cabal/Distribution/Simple/Program/Run.hs 135
               IOEncodingText -> (inputStr, False)
               IOEncodingUTF8 -> (toUTF8 inputStr, True) -- use binary mode for utf8
 
-runProgramInvocation _ _ =
-   die "runProgramInvocation: not yet implemented for this form of invocation"
-
 
 getProgramInvocationOutput :: Verbosity -> ProgramInvocation -> IO String
 getProgramInvocationOutput verbosity
hunk ./cabal/Distribution/Simple/Program/Run.hs 141
   ProgramInvocation {
     progInvokePath  = path,
     progInvokeArgs  = args,
-    progInvokeEnv   = [],
-    progInvokeCwd   = Nothing,
-    progInvokeInput = Nothing,
+    progInvokeEnv   = env,
+    progInvokeCwd   = mcwd,
+    progInvokeInput = minput,
     progInvokeOutputEncoding = encoding
   } = do
   let utf8 = case encoding of IOEncodingUTF8 -> True; _ -> False
hunk ./cabal/Distribution/Simple/Program/Run.hs 149
       decode | utf8      = fromUTF8 . normaliseLineEndings
              | otherwise = id
-  (output, errors, exitCode) <- rawSystemStdInOut verbosity
-                                  path args
-                                  Nothing utf8
+  renv <- mergeEnvironment env
+  (output, errors, exitCode) <- rawSystemStdInOutWithCwdAndEnv verbosity path
+                                    args mcwd (Just renv)
+                                    (fmap (\x -> (x, False)) minput) utf8
   when (exitCode /= ExitSuccess) $
     die errors
   return (decode output)
hunk ./cabal/Distribution/Simple/Program/Run.hs 158
 
 
-getProgramInvocationOutput _ _ =
-   die "getProgramInvocationOutput: not yet implemented for this form of invocation"
-
-
 -- | Like the unix xargs program. Useful for when we've got very long command
 -- lines that might overflow an OS limit on command line length and so you
 -- need to invoke a command multiple times to get all the args in.
hunk ./cabal/Distribution/Simple/Utils.hs 61
 
         -- * running programs
         rawSystemExit,
+        rawSystemExitWithCwd,
         rawSystemExitWithEnv,
hunk ./cabal/Distribution/Simple/Utils.hs 63
+        rawSystemExitWithCwdAndEnv,
         rawSystemStdout,
hunk ./cabal/Distribution/Simple/Utils.hs 65
+        rawSystemStdoutWithCwdAndEnv,
         rawSystemStdInOut,
hunk ./cabal/Distribution/Simple/Utils.hs 67
+        rawSystemStdInOutWithCwdAndEnv,
         maybeExit,
         xargs,
         findProgramLocation,
hunk ./cabal/Distribution/Simple/Utils.hs 159
     , findExecutable )
 import System.Environment
     ( getProgName )
-import System.Cmd
-    ( rawSystem )
 import System.Exit
     ( exitWith, ExitCode(..) )
 import System.FilePath
hunk ./cabal/Distribution/Simple/Utils.hs 352
   res <- cmd
   unless (res == ExitSuccess) $ exitWith res
 
-printRawCommandAndArgs :: Verbosity -> FilePath -> [String] -> IO ()
-printRawCommandAndArgs verbosity path args
- | verbosity >= deafening = print (path, args)
- | verbosity >= verbose   = putStrLn $ unwords (path : args)
- | otherwise              = return ()
-
-printRawCommandAndArgsAndEnv :: Verbosity
-                             -> FilePath
-                             -> [String]
-                             -> [(String, String)]
-                             -> IO ()
-printRawCommandAndArgsAndEnv verbosity path args env
- | verbosity >= deafening = do putStrLn ("Environment: " ++ show env)
+-- | Print status to indicate that a raw command is being run.
+printRawCommandAndArgs :: Verbosity
+                       -> FilePath       -- ^ The command being run.
+                       -> [String]       -- ^ Any arguments
+                       -> Maybe FilePath -- ^ The working directory
+                       -> Maybe [(String,String)] -- ^ The environment
+                       -> IO ()
+printRawCommandAndArgs verbosity path args cwd env
+ | verbosity >= deafening = do case env of
+                                 Just e -> putStrLn ("Environment: " ++ show e)
+                                 Nothing -> return ()
+                               case cwd of
+                                 Just dir -> putStrLn ("Cwd: " ++ dir)
+                                 Nothing -> return ()
                                print (path, args)
  | verbosity >= verbose   = putStrLn $ unwords (path : args)
  | otherwise              = return ()
hunk ./cabal/Distribution/Simple/Utils.hs 370
 
--- Exit with the same exitcode if the subcommand fails
+-- | Exit with the same exitcode if the subcommand fails
 rawSystemExit :: Verbosity -> FilePath -> [String] -> IO ()
hunk ./cabal/Distribution/Simple/Utils.hs 372
-rawSystemExit verbosity path args = do
-  printRawCommandAndArgs verbosity path args
-  hFlush stdout
-  exitcode <- rawSystem path args
-  unless (exitcode == ExitSuccess) $ do
-    debug verbosity $ path ++ " returned " ++ show exitcode
-    exitWith exitcode
+rawSystemExit verbosity path args =
+  doRawSystemExitWithCwdAndEnv verbosity path args Nothing Nothing
+
+-- | Exit with the same exitcode if the subcommand fails
+rawSystemExitWithCwd :: Verbosity -> FilePath -> [String]
+                     -> FilePath -- ^ Directory to run command in
+                     -> IO ()
+rawSystemExitWithCwd verbosity path args cwd =
+   doRawSystemExitWithCwdAndEnv verbosity path args (Just cwd) Nothing
 
hunk ./cabal/Distribution/Simple/Utils.hs 382
+-- | Exit with the same exitcode if the subcommand fails
 rawSystemExitWithEnv :: Verbosity
                      -> FilePath
                      -> [String]
hunk ./cabal/Distribution/Simple/Utils.hs 386
-                     -> [(String, String)]
+                     -> [(String, String)] -- ^ Replace system environment
                      -> IO ()
hunk ./cabal/Distribution/Simple/Utils.hs 388
-rawSystemExitWithEnv verbosity path args env = do
-    printRawCommandAndArgsAndEnv verbosity path args env
+rawSystemExitWithEnv verbosity path args env =
+  doRawSystemExitWithCwdAndEnv verbosity path args Nothing (Just env)
+
+-- | Exit with the same exitcode if the subcommand fails
+rawSystemExitWithCwdAndEnv :: Verbosity
+                           -> FilePath
+                           -> [String]
+                           -> FilePath -- ^ Directory to run in
+                           -> [(String, String)] -- ^ Replace system evironment
+                           -> IO ()
+rawSystemExitWithCwdAndEnv verbosity path args cwd env =
+  doRawSystemExitWithCwdAndEnv verbosity path args (Just cwd) (Just env)
+
+doRawSystemExitWithCwdAndEnv :: Verbosity
+                             -> FilePath
+                             -> [String]
+                             -> Maybe FilePath
+                             -> Maybe [(String, String)]
+                             -> IO ()
+doRawSystemExitWithCwdAndEnv verbosity path args cwd env = do
+    printRawCommandAndArgs verbosity path args cwd env
     hFlush stdout
hunk ./cabal/Distribution/Simple/Utils.hs 410
-    ph <- runProcess path args Nothing (Just env) Nothing Nothing Nothing
+    ph <- runProcess path args cwd env Nothing Nothing Nothing
     exitcode <- waitForProcess ph
     unless (exitcode == ExitSuccess) $ do
         debug verbosity $ path ++ " returned " ++ show exitcode
hunk ./cabal/Distribution/Simple/Utils.hs 421
 -- The output is assumed to be text in the locale encoding.
 --
 rawSystemStdout :: Verbosity -> FilePath -> [String] -> IO String
-rawSystemStdout verbosity path args = do
-  (output, errors, exitCode) <- rawSystemStdInOut verbosity path args
-                                                  Nothing False
+rawSystemStdout verbosity path args =
+   rawSystemStdoutWithCwdAndEnv verbosity path args Nothing Nothing
+
+-- | Run a command in a specified directory and return its output.
+--
+-- The output is assumed to be text in the locale encoding.
+--
+rawSystemStdoutWithCwdAndEnv :: Verbosity
+                             -> FilePath       -- ^ The command
+                             -> [String]       -- ^ The arguments
+                             -> Maybe FilePath -- ^ The working directory
+                             -> Maybe [(String, String)]
+                                      -- ^ Replace the system environment
+                             -> IO String
+rawSystemStdoutWithCwdAndEnv verbosity path args cwd env = do
+  (output, errors, exitCode) <- rawSystemStdInOutWithCwdAndEnv verbosity path
+                                       args cwd env Nothing False
   when (exitCode /= ExitSuccess) $
     die errors
   return output
hunk ./cabal/Distribution/Simple/Utils.hs 451
                   -> Maybe (String, Bool) -- ^ input text and binary mode
                   -> Bool                 -- ^ output in binary mode
                   -> IO (String, String, ExitCode) -- ^ output, errors, exit
-rawSystemStdInOut verbosity path args input outputBinary = do
-  printRawCommandAndArgs verbosity path args
+rawSystemStdInOut verbosity path args input outputBinary =
+   rawSystemStdInOutWithCwdAndEnv verbosity path args Nothing Nothing
+         input outputBinary
+
+rawSystemStdInOutWithCwdAndEnv
+   :: Verbosity
+   -> FilePath -> [String]
+   -> Maybe FilePath -- ^ The working directory
+   -> Maybe [(String, String)] -- ^ Replace the system environment
+   -> Maybe (String, Bool) -- ^ input text and binary mode
+   -> Bool                 -- ^ output in binary mode
+   -> IO (String, String, ExitCode) -- ^ output, errors, exit
+rawSystemStdInOutWithCwdAndEnv verbosity path args cwd env input outputBinary = do
+  printRawCommandAndArgs verbosity path args cwd env 
 
 #ifdef __GLASGOW_HASKELL__
   Exception.bracket
hunk ./cabal/Distribution/Simple/Utils.hs 468
-     (runInteractiveProcess path args Nothing Nothing)
+     (runInteractiveProcess path args cwd env)
      (\(inh,outh,errh,_) -> hClose inh >> hClose outh >> hClose errh)
     $ \(inh,outh,errh,pid) -> do
 
hunk ./cabal/Distribution/Simple/Utils.hs 526
         hPutStr inHandle inputStr
     hClose inHandle
 
+    unless (env == Nothing && cwd == Nothing) $
+      die "raw system execution with cwd or env is only implemented for ghc"
+
     let quote name = "'" ++ concatMap quote_c name ++ "'"
         -- Quote protect a single character
         quote_c '\'' = "\\'"
}

Context:

[Simplify some code in Program.Hpc slightly
Duncan Coutts <dun...@community.haskell.org>**20110726001531
 Ignore-this: d7ea77d1f072f7071fc709e0c9a38ded
] 
[Added Distribution.Simple.Program.Hpc.
Thomas Tuegel <ttue...@gmail.com>**20110719004251
 Ignore-this: a988f4262e4f52c8ae0a3ca5715a636e
] 
[Restore graceful failure upon invoking "cabal test" before "cabal build".
Thomas Tuegel <ttue...@gmail.com>**20110719002218
 Ignore-this: 2096a4cfad17eb67ef26bb15a8b3a066
] 
[Fix executable test suite unit test for improved HPC interface.
Thomas Tuegel <ttue...@gmail.com>**20110718033150
 Ignore-this: b543b01721940b23aac7bd46282425b1
] 
[Generate aggregate coverage statistics from all test suites in package.
Thomas Tuegel <ttue...@gmail.com>**20110718050448
 Ignore-this: bff5f3167ab61da015b8fcb7c4f77cdc
] 
[Invoke HPC using D.S.Program utilities.
Thomas Tuegel <ttue...@gmail.com>**20110718045949
 Ignore-this: 37e1f01f594dd522c5328b397ac0e94d
 This patch also reorganizes the HPC output directories for consistency. All
 files related to HPC are now located in the "dist/hpc" directory.
] 
[Fix cabal haddock for packages with internal dependencies
Duncan Coutts <dun...@community.haskell.org>**20110718235728
 Ignore-this: 86cdab6325a86875e9ae592881b4f54f
] 
[Update cabal sdist to follow the changes in the Cabal lib
Duncan Coutts <dun...@community.haskell.org>**20110717223648
 Ignore-this: 1136aa98cb024a10250ea75ec8633a2c
] 
[Added unit test for test options.
Thomas Tuegel <ttue...@gmail.com>**20110521164529
 Ignore-this: 3dc94c06cdfacf20cf000682370fbf3
] 
[Fixed crash on Windows due to file handle leak.
Thomas Tuegel <ttue...@gmail.com>**20110518030422
 Ignore-this: c94eb903aef9ffcf52394a821d245dda
 Ticket #843. Cabal test crashed when trying to delete a temporary log file
 because 'readFile' reads unnecessarily lazily and was keeping a file handle
 open during attempted deletion. This patch forces the entire file to be read
 so the handle will be closed.
] 
[Stop cabal-install from duplicating test options.
Thomas Tuegel <ttue...@gmail.com>**20110521232047
 Ignore-this: 55b98ab47306178e355cacedc7a5a6d2
] 
[Fix use of multiple test options.
Thomas Tuegel <ttue...@gmail.com>**20110521223029
 Ignore-this: c694ad21faab23abb7157ccec700ccf2
] 
[Don't prefix test output with ">>>".
Thomas Tuegel <ttue...@gmail.com>**20110708035007
 Ignore-this: a9d417eb836c641339a0203d1c36e82e
 Ticket #848. Removing the prefix brings "cabal test" in line with other cabal
 commands, which do not prefix their output, either. Prior to this patch, the
 summary notices which appear before and after each test suite were written to
 the temporary log file along with the stdio from the test executable; this would
 lead to duplicate notices when the contents of the temporary log file are read
 onto the console. After this patch, the summary notices are never written to the
 temporary log file, only to the console and the final log file (which is never
 read by Cabal), removing the confusing duplicate notices.
] 
[Fail gracefully when running "setup test" before "setup build".
Thomas Tuegel <ttue...@gmail.com>**20110303164611
 Ignore-this: a4d818cd7702ddbbbbffc8679abeb85d
] 
[Bump cabal-install version
Duncan Coutts <dun...@community.haskell.org>**20110708013248
 Ignore-this: 16626faad564787fc5ae3808d1e6ccc9
] 
[Bump Cabal lib version
Duncan Coutts <dun...@community.haskell.org>**20110708013245
 Ignore-this: e01c7efbb68856167c227ba118ddce33
] 
[Couple of trivial code changes
Duncan Coutts <dun...@community.haskell.org>**20110708013012
 Ignore-this: b98aaac9e33f8c684cefedcd05d37ee2
] 
[Fix withComponentsLBI and move Components to LocalBuildInfo module
Duncan Coutts <dun...@community.haskell.org>**20110708012122
 Ignore-this: 57217119f7825c9bcd3824a34ecd0c8f
 An annoyance of the current Simple build system is that each phase
 (build, install, etc) can be passed additional HookedBuildInfo which
 gets merged into the PackageDescription. This means that we cannot
 process the PackageDescription up front at configure time and just
 store and reuse it later, we have to work from it each time afresh.
 
 The recent addition of Components (libs, exes, test suites) and a
 topoligical sort of the components in the LocalBuildInfo fell foul
 of this annoyance. The LocalBuildInfo stored the entire component
 which meant they were not updated with the HookedBuildInfo. This
 broke packages with custom Setup.hs scripts that took advantage of
 the HookedBuildInfo feature, including those with configure scripts.
 
 The solution is to store not the list of whole components but the
 list of component names. Then withComponentsLBI retrieves the actual
 components from the PackageDescription which thus includes the
 HookedBuildInfo.
 
 Also moved the Components into an internal module because (for the
 moment at least) it is part of the Simple build system, not part of
 the package description.
] 
[Relax some dependencies
Ian Lynagh <ig...@earth.li>**20110706192619
 Ignore-this: 6353c1d64a2fff3cef3ca0d8a9f2e95e
] 
[Add files needed by the GHC build system
Ian Lynagh <ig...@earth.li>**20110624003654
 Ignore-this: a40dd98104e994d1a1648c3ce2676a45
] 
[Add a dash separator for pid in createTempDirectory and openBinaryTempFile too
Jens Petersen <j...@community.haskell.org>**20110519021658
 Ignore-this: ee0c842388212326579309ac6f93408f
] 
[Update changelog for 1.10.2.0
Duncan Coutts <dun...@community.haskell.org>**20110618190748
 Ignore-this: 64129f45dd16d2d93c82097530dc15d1
] 
[TAG cabal-install merged
Duncan Coutts <dun...@community.haskell.org>**20110619135228
 Ignore-this: 58d670de46a24046d0b869dc2b88e13a
 We now have both the Cabal library and the cabal-install tool
 together in the same repo, each in a subdir.
     
 The idea is that this will make splitting packages and moving
 code between package rather easier in future.
] 
Patch bundle hash:
0d9e4009e9161b3e2f5668e58b27163d7c180f5f
_______________________________________________
cabal-devel mailing list
cabal-devel@haskell.org
http://www.haskell.org/mailman/listinfo/cabal-devel

Reply via email to