From 1471271bf690232b625a763cff91193228fcc3fe Mon Sep 17 00:00:00 2001
From: Mikhail Glushenkov <mikhail.glushenkov@gmail.com>
Date: Fri, 27 Mar 2015 00:06:52 +0100
Subject: [PATCH] Handle more 'strip --version' output formats.

Fixes #2497.
---
 Cabal/Cabal.cabal                             |  2 +
 Cabal/Distribution/Simple/Program/Builtin.hs  | 20 ++------
 Cabal/Distribution/Simple/Program/Internal.hs | 46 +++++++++++++++++++
 Cabal/tests/UnitTests.hs                      |  3 ++
 .../Distribution/Simple/Program/Internal.hs   | 36 +++++++++++++++
 5 files changed, 90 insertions(+), 17 deletions(-)
 create mode 100644 Cabal/Distribution/Simple/Program/Internal.hs
 create mode 100644 Cabal/tests/UnitTests/Distribution/Simple/Program/Internal.hs

diff --git a/Cabal/Cabal.cabal b/Cabal/Cabal.cabal
index 6d73a20db3..9ea3434e59 100644
--- a/Cabal/Cabal.cabal
+++ b/Cabal/Cabal.cabal
@@ -208,6 +208,7 @@ library
     Distribution.Simple.Program.GHC
     Distribution.Simple.Program.HcPkg
     Distribution.Simple.Program.Hpc
+    Distribution.Simple.Program.Internal
     Distribution.Simple.Program.Ld
     Distribution.Simple.Program.Run
     Distribution.Simple.Program.Script
@@ -257,6 +258,7 @@ test-suite unit-tests
   other-modules:
     UnitTests.Distribution.Compat.CreatePipe
     UnitTests.Distribution.Compat.ReadP
+    UnitTests.Distribution.Simple.Program.Internal
     UnitTests.Distribution.Utils.NubList
   main-is: UnitTests.hs
   build-depends:
diff --git a/Cabal/Distribution/Simple/Program/Builtin.hs b/Cabal/Distribution/Simple/Program/Builtin.hs
index a4e56190dd..d844f48b79 100644
--- a/Cabal/Distribution/Simple/Program/Builtin.hs
+++ b/Cabal/Distribution/Simple/Program/Builtin.hs
@@ -47,6 +47,8 @@ module Distribution.Simple.Program.Builtin (
 
 import Distribution.Simple.Program.Find
          ( findProgramOnSearchPath )
+import Distribution.Simple.Program.Internal
+         ( stripExtractVersion )
 import Distribution.Simple.Program.Run
          ( getProgramInvocationOutput, programInvocation )
 import Distribution.Simple.Program.Types
@@ -269,24 +271,8 @@ arProgram = simpleProgram "ar"
 stripProgram :: Program
 stripProgram = (simpleProgram "strip") {
     programFindVersion = \verbosity ->
-      findProgramVersion "--version" selectVersion (lessVerbose verbosity)
+      findProgramVersion "--version" stripExtractVersion (lessVerbose verbosity)
   }
-  where
-    selectVersion str =
-      -- Invoking "strip --version" gives very inconsistent
-      -- results. We look for the first word that starts with a
-      -- number, and try parsing out the first two components of
-      -- it. Non-GNU 'strip' doesn't appear to have a version flag.
-      let numeric ""    = False
-          numeric (x:_) = isDigit x
-      in case dropWhile (not . numeric) (words str) of
-        (ver:_) ->
-          -- take the first two version components
-          let isDot         = (== '.')
-              (major, rest) = break isDot ver
-              minor         = takeWhile (not . isDot) (dropWhile isDot rest)
-          in major ++ "." ++ minor
-        _ -> ""
 
 hsc2hsProgram :: Program
 hsc2hsProgram = (simpleProgram "hsc2hs") {
diff --git a/Cabal/Distribution/Simple/Program/Internal.hs b/Cabal/Distribution/Simple/Program/Internal.hs
new file mode 100644
index 0000000000..76e50376c5
--- /dev/null
+++ b/Cabal/Distribution/Simple/Program/Internal.hs
@@ -0,0 +1,46 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Distribution.Simple.Program.Internal
+--
+-- Maintainer  :  cabal-devel@haskell.org
+-- Portability :  portable
+--
+-- Internal utilities used by Distribution.Simple.Program.*.
+
+module Distribution.Simple.Program.Internal (
+    stripExtractVersion,
+  ) where
+
+import Data.Char (isDigit)
+import Data.List (isPrefixOf, isSuffixOf)
+
+-- | Extract the version number from the output of 'strip --version'.
+--
+-- Invoking "strip --version" gives very inconsistent results. We ignore
+-- everything in parentheses (see #2497), look for the first word that starts
+-- with a number, and try parsing out the first two components of it. Non-GNU
+-- 'strip' doesn't appear to have a version flag.
+stripExtractVersion :: String -> String
+stripExtractVersion str =
+  let numeric ""    = False
+      numeric (x:_) = isDigit x
+
+      -- Filter out everything in parentheses.
+      filterPar' :: Int -> [String] -> [String]
+      filterPar' _ []                   = []
+      filterPar' n (x:xs)
+        | n >= 0 && "(" `isPrefixOf` x = filterPar' (n+1) ((tail x):xs)
+        | n >  0 && ")" `isSuffixOf` x = filterPar' (n-1) xs
+        | n >  0                       = filterPar' n xs
+        | otherwise                    = x:filterPar' n xs
+
+      filterPar = filterPar' 0
+
+  in case dropWhile (not . numeric) (filterPar . words $ str) of
+    (ver:_) ->
+      -- take the first two version components
+      let isDot         = (== '.')
+          (major, rest) = break isDot ver
+          minor         = takeWhile isDigit (dropWhile isDot rest)
+      in major ++ "." ++ minor
+    _ -> ""
diff --git a/Cabal/tests/UnitTests.hs b/Cabal/tests/UnitTests.hs
index fe78268b79..6f22262b00 100644
--- a/Cabal/tests/UnitTests.hs
+++ b/Cabal/tests/UnitTests.hs
@@ -6,6 +6,7 @@ import Test.Tasty
 
 import qualified UnitTests.Distribution.Compat.CreatePipe
 import qualified UnitTests.Distribution.Compat.ReadP
+import qualified UnitTests.Distribution.Simple.Program.Internal
 import qualified UnitTests.Distribution.Utils.NubList
 
 tests :: TestTree
@@ -14,6 +15,8 @@ tests = testGroup "Unit Tests" $
         UnitTests.Distribution.Compat.ReadP.tests
     , testGroup "Distribution.Compat.CreatePipe"
         UnitTests.Distribution.Compat.CreatePipe.tests
+    , testGroup "Distribution.Simple.Program.Internal"
+        UnitTests.Distribution.Simple.Program.Internal.tests
     , testGroup "Distribution.Utils.NubList"
         UnitTests.Distribution.Utils.NubList.tests
     ]
diff --git a/Cabal/tests/UnitTests/Distribution/Simple/Program/Internal.hs b/Cabal/tests/UnitTests/Distribution/Simple/Program/Internal.hs
new file mode 100644
index 0000000000..4766cbb36c
--- /dev/null
+++ b/Cabal/tests/UnitTests/Distribution/Simple/Program/Internal.hs
@@ -0,0 +1,36 @@
+module UnitTests.Distribution.Simple.Program.Internal
+    ( tests
+    ) where
+
+import Distribution.Simple.Program.Internal ( stripExtractVersion )
+
+import Test.Tasty
+import Test.Tasty.HUnit
+
+v :: String
+v = "GNU strip (GNU Binutils; openSUSE 13.2) 2.24.0.20140403-6.1\nCopyright 2013\
+  \ Free Software Foundation, Inc.\nThis program is free software; you may\
+  \ redistribute it under the terms of\nthe GNU General Public License version 3\
+  \ or (at your option) any later version.\nThis program has absolutely no\
+  \ warranty.\n"
+
+v' :: String
+v' = "GNU strip 2.17.50.0.6-26.el5 20061020"
+
+v'' :: String
+v'' = "GNU strip (openSUSE-13.2) 2.23.50.0.6-26.el5 20061020"
+
+v''' :: String
+v''' = "GNU strip (GNU (Binutils for) Ubuntu 12.04 ) 2.22"
+
+tests :: [TestTree]
+tests =
+    [ testCase "Handles parentheses" $
+      (stripExtractVersion v)    @=? "2.24"
+    , testCase "Handles dashes and alphabetic characters" $
+      (stripExtractVersion v')   @=? "2.17"
+    , testCase "Handles single-word parenthetical expressions" $
+      (stripExtractVersion v'')  @=? "2.23"
+    , testCase "Handles nested parentheses" $
+      (stripExtractVersion v''') @=? "2.22"
+    ]
-- 
GitLab