configure.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. #!/usr/bin/env python
  2. #-------------------------------------------------------------------------------------------------------
  3. # Copyright (C) Microsoft. All rights reserved.
  4. # Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  5. #-------------------------------------------------------------------------------------------------------
  6. #
  7. # TODO build.sh stopped using this script, is it needed?
  8. #
  9. from __future__ import print_function
  10. import hashlib
  11. import os
  12. import shutil
  13. import sys
  14. import tarfile
  15. import urllib
  16. import urllib2
  17. from zipfile import ZipFile
  18. from argparse import ArgumentParser
  19. # Parse Makefile.in to find the OBJECTS = ... list of object files
  20. # This is the officially recommended way of integrating ICU into a large project's build system
  21. def get_sources(icuroot, mkin_path):
  22. # ignore these files, similar to Node
  23. ignore = [
  24. "source/tools/toolutil/udbgutil.cpp",
  25. "source/tools/toolutil/dbgutil.cpp",
  26. ]
  27. ignore = [os.path.join(icuroot, os.path.normpath(source)) for source in ignore]
  28. def get_source(object_path):
  29. base = os.path.splitext(object_path)[0]
  30. cpp = base + ".cpp"
  31. c = base + ".c"
  32. # return None if we find a source but explicitly exclude it, compared
  33. # to raising an exception if a source is referenced that doesn't exist,
  34. # since that is more likely to be an issue with the source/ folder
  35. if cpp in ignore or c in ignore:
  36. return None
  37. if os.path.isfile(cpp):
  38. return cpp
  39. elif os.path.isfile(c):
  40. return c
  41. raise Exception("%s has no corresponding source file" % object_path)
  42. with open(mkin_path, "r") as mkin_contents:
  43. in_objs = False
  44. sources = []
  45. for line in mkin_contents:
  46. line = line.strip()
  47. if line[:7] == "OBJECTS":
  48. in_objs = True
  49. # trim " = " in "OBJECTS = {object files}"
  50. line = line[10:]
  51. elif in_objs == True and len(line) == 0:
  52. # done with OBJECTS, return
  53. return sources
  54. if in_objs == True:
  55. # trim " \" in "{object files} \"
  56. linelen = len(line)
  57. line = line[:linelen - 1].strip() if line[linelen - 1] == "\\" else line
  58. objects = map(lambda o: os.path.join(os.path.dirname(mkin_path), o), line.split())
  59. cpps = map(get_source, objects)
  60. cpps = filter(lambda cpp: cpp != None, cpps)
  61. sources.extend(cpps)
  62. # should have returned by now
  63. raise Exception("Could not extract sources from %s" % mkin_path)
  64. def get_headers(icuroot, headers_path):
  65. # ignore these files, similar to Node
  66. ignore = [
  67. "source/tools/toolutil/udbgutil.h",
  68. "source/tools/toolutil/dbgutil.h",
  69. ]
  70. ignore = [os.path.join(icuroot, os.path.normpath(source)) for source in ignore]
  71. if not os.path.isdir(headers_path):
  72. raise Exception("%s is not a valid headers path" % headers_path)
  73. headers = map(lambda h: os.path.join(headers_path, h), os.listdir(headers_path))
  74. headers = filter(lambda h: os.path.splitext(h)[1] == ".h", headers) # only include .h files
  75. headers = filter(lambda h: h not in ignore, headers) # don't include ignored headers
  76. return headers
  77. def create_msvc_props(chakra_icu_root, icu_sources_root, version):
  78. prelude = """<?xml version="1.0" encoding="utf-8"?>
  79. <!-- DO NOT EDIT THIS FILE. It is auto-generated by $ChakraCore/tools/icu/%s -->
  80. <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  81. <PropertyGroup>""" % os.path.basename(__file__)
  82. prop_template = """
  83. <Icu{0}{1}>
  84. {2}
  85. </Icu{0}{1}>"""
  86. conclusion = """
  87. </PropertyGroup>
  88. </Project>
  89. """
  90. joiner = ";\n "
  91. def add_props(propfile, deproot, dep):
  92. sources = get_sources(icu_sources_root, os.path.join(deproot, dep, "Makefile.in"))
  93. sources_prop = prop_template.format(dep.capitalize(), "Sources", joiner.join(sources))
  94. propfile.write(sources_prop)
  95. headers = get_headers(icu_sources_root, os.path.join(deproot, dep))
  96. headers_prop = prop_template.format(dep.capitalize(), "Headers", joiner.join(headers))
  97. propfile.write(headers_prop)
  98. with open(os.path.join(chakra_icu_root, "Chakra.ICU.props"), "w") as propfile:
  99. propfile.write(prelude)
  100. # Write all of the sources and header files to Icu${Dep}${DepKind}, such as IcuCommonSources
  101. sourceroot = os.path.join(icu_sources_root, "source")
  102. for dep in ["common", "i18n", "stubdata"]:
  103. add_props(propfile, sourceroot, dep)
  104. # tools are handled somewhat differently, since theyre in source/tools
  105. toolsroot = os.path.join(sourceroot, "tools")
  106. for dep in ["toolutil", "genccode"]:
  107. add_props(propfile, toolsroot, dep)
  108. version_parts = version.split(".")
  109. no_newline_prop_template = "\n <Icu{0}>{1}</Icu{0}>"
  110. propfile.write(no_newline_prop_template.format("VersionMajor", version_parts[0]))
  111. propfile.write(no_newline_prop_template.format("VersionMinor", version_parts[1] if len(version_parts) > 1 else "0"))
  112. propfile.write(no_newline_prop_template.format("SourceDirectory", sourceroot))
  113. include_dirs = [os.path.join(sourceroot, component) for component in ["common", "i18n"]]
  114. propfile.write(prop_template.format("Include", "Directories", joiner.join(include_dirs)))
  115. propfile.write(conclusion)
  116. def download_icu(icuroot, version, yes):
  117. # download either the zip or tar, depending on the os
  118. extension = "zip" if os.name == "nt" else "tgz"
  119. archive_file = "icu4c-{0}-src.{1}".format(version.replace(".", "_"), extension)
  120. # Use this in future, currently the SHA hash file name for version 63.2 seems to not include version name.
  121. #hash_file = "icu4c-{0}-src.{1}.asc".format(version.replace(".", "_"), extension)
  122. hash_file = "icu4c-SHASUM512.txt.asc"
  123. archive_url = "https://github.com/unicode-org/icu/releases/download/release-{0}/{1}".format(version.replace(".", "-"), archive_file)
  124. hash_url = "https://github.com/unicode-org/icu/releases/download/release-{0}/{1}".format(version.replace(".", "-"), hash_file)
  125. #print(archive_file)
  126. #print(hash_file)
  127. #print(archive_url)
  128. #print(hash_url)
  129. license_confirmation = """
  130. {1}
  131. This script downloads ICU from {0}.
  132. It is licensed to you by its publisher, not Microsoft.
  133. Microsoft is not responsible for the software.
  134. Your installation and use of ICU is subject to the publisher's terms,
  135. which are available here: http://www.unicode.org/copyright.html#License
  136. {1}
  137. """.format(archive_url, "-" * 80)
  138. if not yes:
  139. print(license_confirmation)
  140. response = raw_input("Do you agree to these terms? [Y/n] ")
  141. if response != "" and response != "y" and response != "Y":
  142. sys.exit(0)
  143. print("Downloading ICU from %s" % archive_url)
  144. archive_path = urllib.urlretrieve(archive_url)[0]
  145. print("Downloaded ICU to %s" % archive_path)
  146. # check the hash of the download zipfile/tarball
  147. checksum = ""
  148. with open(archive_path, "rb") as download:
  149. hashAlgorithm = hashlib.sha512()
  150. hashAlgorithm.update(download.read())
  151. checksum = hashAlgorithm.hexdigest()
  152. hash_path = os.path.join(icuroot, hash_file)
  153. hash_request = urllib2.urlopen(hash_url)
  154. allHashes = hash_request.read().decode("ascii").split("\n")
  155. relevant_hash = filter(lambda line: line[len(line) - len(archive_file):] == archive_file, allHashes)
  156. if len(relevant_hash) != 1:
  157. raise Exception("Could not find hash for {0} in {1}".format(archive_file, hash_url))
  158. correct_hash = relevant_hash[0]
  159. correct_hash = correct_hash.split(" ")[0]
  160. if (correct_hash == checksum):
  161. print("Hash checksums match, continuing")
  162. return archive_path
  163. else:
  164. raise Exception("Hash checksums do not match. Expected {0}, got {1}".format(correct_hash, checksum))
  165. def extract_icu(icuroot, archive_path):
  166. tempdir = os.path.normpath(os.path.join(icuroot, "temp"))
  167. print("Extracting ICU to %s" % tempdir)
  168. opener = ZipFile if os.name == "nt" else tarfile.open
  169. with opener(archive_path, "r") as archive:
  170. archive.extractall(tempdir)
  171. icu_folder = os.path.join(icuroot, "icu")
  172. if os.path.isdir(icu_folder):
  173. shutil.rmtree(icu_folder)
  174. print("Extraction successful, ICU will be located at %s" % icu_folder)
  175. shutil.move(os.path.join(tempdir, "icu"), icu_folder)
  176. shutil.rmtree(tempdir)
  177. def main():
  178. chakra_icu_root = os.path.normpath(os.path.join(os.path.realpath(__file__), "..", "..", "..", "deps", "Chakra.ICU"))
  179. argparser = ArgumentParser(description = "Download and set up ICU for use in ChakraCore")
  180. argparser.add_argument("-y", "--yes", action = "store_true", help = "Skip ICU License prompt text")
  181. argparser.add_argument("version", help = "ICU version to download. Not compatible with --archive", default = "61.1", nargs = "?")
  182. argparser.add_argument("-i", "--icu-root",
  183. help = "Path to directory to extract ICU to. Resulting directory will contain a single subfolder, 'icu', which contains ICU's source tree",
  184. default = chakra_icu_root
  185. )
  186. argparser.add_argument("-a", "--archive", help = "Path to icu.zip (Windows) or icu.tar (POSIX) that you have already downloaded")
  187. args = argparser.parse_args()
  188. archive_path = args.archive
  189. if args.version is not None and args.archive is None:
  190. archive_path = download_icu(args.icu_root, args.version, args.yes)
  191. extract_icu(args.icu_root, archive_path)
  192. if os.name == "nt":
  193. create_msvc_props(chakra_icu_root, os.path.join(args.icu_root, "icu"), args.version)
  194. if __name__ == "__main__":
  195. main()