blob: 4423902d79e8be8615beb949b6de563e5d7f9b21 [file] [log] [blame]
#!/usr/bin/env python
#-------------------------------------------------------------------------------------------------------
# Copyright (C) Microsoft. All rights reserved.
# Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
#-------------------------------------------------------------------------------------------------------
from __future__ import print_function
import hashlib
import os
import shutil
import sys
import tarfile
import urllib
import urllib2
from zipfile import ZipFile
from argparse import ArgumentParser
# Parse Makefile.in to find the OBJECTS = ... list of object files
# This is the officially recommended way of integrating ICU into a large project's build system
def get_sources(icuroot, mkin_path):
# ignore these files, similar to Node
ignore = [
"source/tools/toolutil/udbgutil.cpp",
"source/tools/toolutil/dbgutil.cpp",
]
ignore = [os.path.join(icuroot, os.path.normpath(source)) for source in ignore]
def get_source(object_path):
base = os.path.splitext(object_path)[0]
cpp = base + ".cpp"
c = base + ".c"
# return None if we find a source but explicitly exclude it, compared
# to raising an exception if a source is referenced that doesn't exist,
# since that is more likely to be an issue with the source/ folder
if cpp in ignore or c in ignore:
return None
if os.path.isfile(cpp):
return cpp
elif os.path.isfile(c):
return c
raise Exception("%s has no corresponding source file" % object_path)
with open(mkin_path, "r") as mkin_contents:
in_objs = False
sources = []
for line in mkin_contents:
line = line.strip()
if line[:7] == "OBJECTS":
in_objs = True
# trim " = " in "OBJECTS = {object files}"
line = line[10:]
elif in_objs == True and len(line) == 0:
# done with OBJECTS, return
return sources
if in_objs == True:
# trim " \" in "{object files} \"
linelen = len(line)
line = line[:linelen - 1].strip() if line[linelen - 1] == "\\" else line
objects = map(lambda o: os.path.join(os.path.dirname(mkin_path), o), line.split())
cpps = map(get_source, objects)
cpps = filter(lambda cpp: cpp != None, cpps)
sources.extend(cpps)
# should have returned by now
raise Exception("Could not extract sources from %s" % mkin_path)
def get_headers(icuroot, headers_path):
# ignore these files, similar to Node
ignore = [
"source/tools/toolutil/udbgutil.h",
"source/tools/toolutil/dbgutil.h",
]
ignore = [os.path.join(icuroot, os.path.normpath(source)) for source in ignore]
if not os.path.isdir(headers_path):
raise Exception("%s is not a valid headers path" % headers_path)
headers = map(lambda h: os.path.join(headers_path, h), os.listdir(headers_path))
headers = filter(lambda h: os.path.splitext(h)[1] == ".h", headers) # only include .h files
headers = filter(lambda h: h not in ignore, headers) # don't include ignored headers
return headers
def create_msvc_props(chakra_icu_root, icu_sources_root, version):
prelude = """<?xml version="1.0" encoding="utf-8"?>
<!-- DO NOT EDIT THIS FILE. It is auto-generated by $ChakraCore/tools/icu/%s -->
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>""" % os.path.basename(__file__)
prop_template = """
<Icu{0}{1}>
{2}
</Icu{0}{1}>"""
conclusion = """
</PropertyGroup>
</Project>
"""
joiner = ";\n "
def add_props(propfile, deproot, dep):
sources = get_sources(icu_sources_root, os.path.join(deproot, dep, "Makefile.in"))
sources_prop = prop_template.format(dep.capitalize(), "Sources", joiner.join(sources))
propfile.write(sources_prop)
headers = get_headers(icu_sources_root, os.path.join(deproot, dep))
headers_prop = prop_template.format(dep.capitalize(), "Headers", joiner.join(headers))
propfile.write(headers_prop)
with open(os.path.join(chakra_icu_root, "Chakra.ICU.props"), "w") as propfile:
propfile.write(prelude)
# Write all of the sources and header files to Icu${Dep}${DepKind}, such as IcuCommonSources
sourceroot = os.path.join(icu_sources_root, "source")
for dep in ["common", "i18n", "stubdata"]:
add_props(propfile, sourceroot, dep)
# tools are handled somewhat differently, since theyre in source/tools
toolsroot = os.path.join(sourceroot, "tools")
for dep in ["toolutil", "genccode"]:
add_props(propfile, toolsroot, dep)
version_parts = version.split(".")
no_newline_prop_template = "\n <Icu{0}>{1}</Icu{0}>"
propfile.write(no_newline_prop_template.format("VersionMajor", version_parts[0]))
propfile.write(no_newline_prop_template.format("VersionMinor", version_parts[1] if len(version_parts) > 1 else "0"))
propfile.write(no_newline_prop_template.format("SourceDirectory", sourceroot))
include_dirs = [os.path.join(sourceroot, component) for component in ["common", "i18n"]]
propfile.write(prop_template.format("Include", "Directories", joiner.join(include_dirs)))
propfile.write(conclusion)
def download_icu(icuroot, version, yes):
# download either the zip or tar, depending on the os
extension = "zip" if os.name == "nt" else "tgz"
archive_file = "icu4c-{0}-src.{1}".format(version.replace(".", "_"), extension)
# Use this in future, currently the SHA hash file name for version 63.2 seems to not include version name.
#hash_file = "icu4c-{0}-src.{1}.asc".format(version.replace(".", "_"), extension)
hash_file = "icu4c-SHASUM512.txt.asc"
archive_url = "https://github.com/unicode-org/icu/releases/download/release-{0}/{1}".format(version.replace(".", "-"), archive_file)
hash_url = "https://github.com/unicode-org/icu/releases/download/release-{0}/{1}".format(version.replace(".", "-"), hash_file)
#print(archive_file)
#print(hash_file)
#print(archive_url)
#print(hash_url)
license_confirmation = """
{1}
This script downloads ICU from {0}.
It is licensed to you by its publisher, not Microsoft.
Microsoft is not responsible for the software.
Your installation and use of ICU is subject to the publisher's terms,
which are available here: http://www.unicode.org/copyright.html#License
{1}
""".format(archive_url, "-" * 80)
if not yes:
print(license_confirmation)
response = raw_input("Do you agree to these terms? [Y/n] ")
if response != "" and response != "y" and response != "Y":
sys.exit(0)
print("Downloading ICU from %s" % archive_url)
archive_path = urllib.urlretrieve(archive_url)[0]
print("Downloaded ICU to %s" % archive_path)
# check the hash of the download zipfile/tarball
checksum = ""
with open(archive_path, "rb") as download:
hashAlgorithm = hashlib.sha512()
hashAlgorithm.update(download.read())
checksum = hashAlgorithm.hexdigest()
hash_path = os.path.join(icuroot, hash_file)
hash_request = urllib2.urlopen(hash_url)
allHashes = hash_request.read().decode("ascii").split("\n")
relevant_hash = filter(lambda line: line[len(line) - len(archive_file):] == archive_file, allHashes)
if len(relevant_hash) != 1:
raise Exception("Could not find hash for {0} in {1}".format(archive_file, hash_url))
correct_hash = relevant_hash[0]
correct_hash = correct_hash.split(" ")[0]
if (correct_hash == checksum):
print("Hash checksums match, continuing")
return archive_path
else:
raise Exception("Hash checksums do not match. Expected {0}, got {1}".format(correct_hash, checksum))
def extract_icu(icuroot, archive_path):
tempdir = os.path.normpath(os.path.join(icuroot, "temp"))
print("Extracting ICU to %s" % tempdir)
opener = ZipFile if os.name == "nt" else tarfile.open
with opener(archive_path, "r") as archive:
archive.extractall(tempdir)
icu_folder = os.path.join(icuroot, "icu")
if os.path.isdir(icu_folder):
shutil.rmtree(icu_folder)
print("Extraction successful, ICU will be located at %s" % icu_folder)
shutil.move(os.path.join(tempdir, "icu"), icu_folder)
shutil.rmtree(tempdir)
def main():
chakra_icu_root = os.path.normpath(os.path.join(os.path.realpath(__file__), "..", "..", "..", "deps", "Chakra.ICU"))
argparser = ArgumentParser(description = "Download and set up ICU for use in ChakraCore")
argparser.add_argument("-y", "--yes", action = "store_true", help = "Skip ICU License prompt text")
argparser.add_argument("version", help = "ICU version to download. Not compatible with --archive", default = "61.1", nargs = "?")
argparser.add_argument("-i", "--icu-root",
help = "Path to directory to extract ICU to. Resulting directory will contain a single subfolder, 'icu', which contains ICU's source tree",
default = chakra_icu_root
)
argparser.add_argument("-a", "--archive", help = "Path to icu.zip (Windows) or icu.tar (POSIX) that you have already downloaded")
args = argparser.parse_args()
archive_path = args.archive
if args.version is not None and args.archive is None:
archive_path = download_icu(args.icu_root, args.version, args.yes)
extract_icu(args.icu_root, archive_path)
if os.name == "nt":
create_msvc_props(chakra_icu_root, os.path.join(args.icu_root, "icu"), args.version)
if __name__ == "__main__":
main()