/haskell_pupeno.py

https://bitbucket.org/russel/scons_haskell · Python · 149 lines · 97 code · 22 blank · 30 comment · 13 complexity · 7737d9fdbcf35d6e797382c1bf919818 MD5 · raw file

  1. # -*- mode:python; coding:utf-8; -*-
  2. # A SCons tool to enable compilation of Haskell in SCons.
  3. #
  4. # Copyright © 2005 José Pablo Ezequiel "Pupeno" Fernández Silva
  5. # Copyright © 2009 Russel Winder
  6. #
  7. # This program is free software: you can redistribute it and/or modify it under the terms of the GNU
  8. # General Public License as published by the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
  12. # the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
  13. # License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License along with this program. If not, see
  16. # <http://www.gnu.org/licenses/>.
  17. #
  18. # Original this code was licenced under GPLv2. This fork is relicenced under GPLv3 as is permitted.
  19. import SCons.Tool
  20. import SCons.Action
  21. from SCons.Scanner import Scanner
  22. from SCons.Defaults import ObjSourceScan, ArAction
  23. import os.path
  24. import string
  25. def generate(env):
  26. env["HS"] = env.Detect("ghc") or "ghc"
  27. env["HSLINK"] = "$HS $_LIBS $SOURCES -o $TARGET"
  28. env["HSCOM"] = "$HS $_IMPORTS $_LIBS -c $SOURCE -o $TARGET"
  29. env["_IMPORTS"] = "${_concat(IMPORTSPREFIX, LIBPATH, IMPORTSSUFFIX, __env__)}"
  30. env["IMPORTSPREFIX"] = "-i"
  31. env["IMPORTSSUFFIX"] = ""
  32. env["_LIBS"] = "${_concat(LIBSPREFIX, LIBS, LIBSSUFFIX, __env__)}"
  33. env["LIBSPREFIX"] = "-package "
  34. env["LIBSSUFFIX"] = ""
  35. haskellSuffixes = [".hs", ".lhs"]
  36. compileAction = SCons.Action.Action("$HSCOM")
  37. linkAction = SCons.Action.Action("$HSLINK")
  38. def addHaskellInterface(target, source, env):
  39. """ Add the .hi target with the same name as the object file. """
  40. targetName = os.path.splitext(str(target[0]))[0]
  41. return (target + [ targetName + ".hi"], source)
  42. def importedModules(node, env, path):
  43. """ Use ghc to find all the imported modules. """
  44. #print "Figuring out dependencies for " + str(node)
  45. def removeFile(fileName, errmsg = None):
  46. """ Try to remove fileName, returns true on success, false otherwise. """
  47. if os.path.exists(fileName):
  48. try:
  49. os.remove(fileName)
  50. except OSError:
  51. print "Unable to remove '%s'." % fileName
  52. return False
  53. return True
  54. # Generate the name of the file that is going to contain the dependency mappings.
  55. fileName = os.path.join(os.path.dirname(str(node)),
  56. "." + os.path.basename(str(node)) + ".dep")
  57. # Just in case the file already exist, to avoid the creation of a .bak file, delete it.
  58. if not removeFile(fileName):
  59. print "Dependencies will not be calculated."
  60. return []
  61. # Build the command to obtain the dependency mapping from ghc.
  62. command = ["ghc", "-M", "-optdep-f", "-optdep" + fileName]
  63. if env._dict.has_key("LIBPATH"):
  64. command += ["-i" + string.join(env["LIBPATH"], ":")]
  65. command += [str(node)]
  66. command = string.join(command)
  67. commandIn, commandOut = os.popen4(command, "r")
  68. errorMessage = commandOut.read()
  69. commandIn.close()
  70. commandOut.read()
  71. if(errorMessage != ""):
  72. print "An error ocurred running `%s`:" % command
  73. for line in string.split(errorMessage, "\n"):
  74. print ">" + line
  75. print "Dependencies will not be calculated."
  76. removeFile(fileName)
  77. return []
  78. try:
  79. file = open(fileName, "r")
  80. fileContents = file.read()
  81. file.close()
  82. except:
  83. print "Unable to open '%s'." % fileName
  84. print "Dependencies will not be calculated."
  85. removeFile(fileName)
  86. return []
  87. fileContents = string.split(fileContents, "\n")
  88. deps = []
  89. for line in fileContents:
  90. #print "deps=%s." % str(deps)
  91. if len(line) > 0 and line[0] != "#":
  92. files = string.split(line, ":")
  93. target = string.strip(files[0])
  94. source = string.strip(files[1])
  95. if source != str(node): # and os.path.splitext(source)[1] != ".hi":
  96. deps += [os.path.basename(source)]
  97. #if os.path.splitext(target)[0] != os.path.splitext(str(node))[0]:
  98. # deps += [os.path.basename(target)]
  99. #print " %s depends on %s." % (target, source)
  100. removeFile(fileName)
  101. #print "%s depends on %s." % (str(node), str(deps))
  102. return deps
  103. haskellScanner = Scanner(function = importedModules,
  104. name = "HaskellScanner",
  105. skeys = haskellSuffixes,
  106. recursive = False)
  107. haskellProgram = SCons.Builder.Builder(action = linkAction,
  108. prefix = "$PROGPREFIX",
  109. suffix = "$PROGSUFFIX",
  110. src_suffix = "$OBJSUFFIX",
  111. src_builder = "HaskellObject")
  112. env["BUILDERS"]["HaskellProgram"] = haskellProgram
  113. haskellLibrary = SCons.Builder.Builder(action = SCons.Defaults.ArAction,
  114. prefix = "$LIBPREFIX",
  115. suffix = "$LIBSUFFIX",
  116. src_suffix = "$OBJSUFFIX",
  117. src_builder = "HaskellObject")
  118. env["BUILDERS"]["HaskellLibrary"] = haskellLibrary
  119. haskellObject = SCons.Builder.Builder(action = compileAction,
  120. emitter = addHaskellInterface,
  121. prefix = "$OBJPREFIX",
  122. suffix = "$OBJSUFFIX",
  123. src_suffix = haskellSuffixes,
  124. source_scanner = haskellScanner)
  125. env["BUILDERS"]["HaskellObject"] = haskellObject
  126. def exists(env):
  127. return env.Detect(["ghc"])