From 7cb4aad7f3c4da2910146d8ff7750cb27ccb664f Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Wed, 12 Nov 2014 17:13:14 -0800 Subject: [PATCH 01/10] build: i18n: Autodownload ICU, Add a test. This is to implement https://github.com/joyent/node/issues/7676#issuecomment-64704230 * make `--with-intl=none` the default * Download, verify (md5), unpack ICU's zip if not there * update docs * add a test There's a "list" of URLs being used, but right now only the first is picked up. The logic works something like this: * if there is no directory `deps/icu`, * if no zip file (currently `icu4c-54_1-src.zip`), * download zip file (icu-project.org -> sf.net) * verify the MD5 sum of the zipfile * if bad, print error and exit * unpack the zipfile into `deps/icu` * if `deps/icu` now exists, use it, else fail with help text Also: * refactor some code into tools/configure.d/nodedownload.py * add `intl-none` option for `vcbuild.bat` To rebuild `deps/icu-small` - (not currently checked in) ``` bash tools/icu/prepare-icu-source.sh ``` Reduce space by about 1MB with ICU 54 (over without this patch). Also trims a few other source files, but only conditional on the exact ICU version used. This is to future-proof - a file that is unneeded now may be needed in future ICUs. --- .gitignore | 2 + README.md | 64 ++++++++++++++++++-- configure | 73 ++++++++++++++++++++--- test/simple/test-intl.js | 62 ++++++++++++++++++++ tools/configure.d/nodedownload.py | 57 ++++++++++++++++++ tools/icu/icu-generic.gyp | 91 +++++++++++++++++++++++++++-- tools/icu/icu_small.json | 3 +- tools/icu/prepare-icu-source.sh | 57 ++++++++++++++++++ tools/icu/trim-uncompiled-source.sh | 21 +++++++ vcbuild.bat | 4 +- 10 files changed, 415 insertions(+), 19 deletions(-) create mode 100644 test/simple/test-intl.js create mode 100644 tools/configure.d/nodedownload.py create mode 100644 tools/icu/prepare-icu-source.sh create mode 100644 tools/icu/trim-uncompiled-source.sh diff --git a/.gitignore b/.gitignore index 6581dee9d1f..39775b1767d 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,8 @@ ipch/ email.md deps/v8-* deps/icu +deps/icu*.zip +deps/icu*.tgz ./node_modules .svn/ diff --git a/README.md b/README.md index 0032c63c073..88588ecbf52 100644 --- a/README.md +++ b/README.md @@ -83,20 +83,76 @@ make doc man doc/node.1 ``` -### To build `Intl` (ECMA-402) support: +### `Intl` (ECMA-402) support: -*Note:* more docs, including how to reduce disk footprint, are on +[Intl](https://github.com/joyent/node/wiki/Intl) support is not +enabled by default. + +#### "small" (English only) support + +This option will build with "small" (English only) support, but +the full `Intl` (ECMA-402) APIs. It will download the ICU library +as needed. + +Unix/Macintosh: + +```sh +./configure --with-intl=small-icu +``` + +Windows: + +```sh +vcbuild small-icu +``` + +The `small-icu` mode builds +with English-only data. You can add full data at runtime. + +*Note:* more docs are on [the wiki](https://github.com/joyent/node/wiki/Intl). +#### Build with full ICU support (all locales supported by ICU): + +*Note*, this may download ICU if you don't have an ICU in `deps/icu` + +Unix/Macintosh: + +```sh +./configure --with-intl=full-icu +``` + +Windows: + +```sh +vcbuild full-icu +``` + +#### Build with no Intl support `:-(` + +The `Intl` object will not be available. + +Unix/Macintosh: + +```sh +./configure --with-intl=none +``` + +Windows: + +```sh +vcbuild intl-none +``` + #### Use existing installed ICU (Unix/Macintosh only): ```sh pkg-config --modversion icu-i18n && ./configure --with-intl=system-icu ``` -#### Build ICU from source: +#### Build with a specific ICU: -First: Unpack latest ICU +First: Unpack latest ICU to `deps/icu` [icu4c-**##.#**-src.tgz](http://icu-project.org/download) (or `.zip`) as `deps/icu` (You'll have: `deps/icu/source/...`) diff --git a/configure b/configure index c558f7f8dd7..37d8a4b6804 100755 --- a/configure +++ b/configure @@ -6,6 +6,7 @@ import re import shlex import subprocess import sys +import shutil CC = os.environ.get('CC', 'cc') @@ -13,6 +14,10 @@ root_dir = os.path.dirname(__file__) sys.path.insert(0, os.path.join(root_dir, 'tools', 'gyp', 'pylib')) from gyp.common import GetFlavor +# imports in tools/configure.d +sys.path.insert(0, os.path.join(root_dir, 'tools', 'configure.d')) +import nodedownload + # parse our options parser = optparse.OptionParser() @@ -712,6 +717,34 @@ def glob_to_var(dir_base, dir_sub): return list def configure_intl(o): + icus = [ + { + 'url': 'http://download.icu-project.org/files/icu4c/54.1/icu4c-54_1-src.zip', + # from https://ssl.icu-project.org/files/icu4c/54.1/icu4c-src-54_1.md5: + 'md5': '6b89d60e2f0e140898ae4d7f72323bca', + }, + ] + def icu_download(path): + # download ICU, if needed + for icu in icus: + url = icu['url'] + md5 = icu['md5'] + local = url.split('/')[-1] + targetfile = os.path.join(root_dir, 'deps', local) + if not os.path.isfile(targetfile): + nodedownload.retrievefile(url, targetfile) + else: + print ' Re-using existing %s' % targetfile + if os.path.isfile(targetfile): + sys.stdout.write(' Checking file integrity with MD5:\r') + gotmd5 = nodedownload.md5sum(targetfile) + print ' MD5: %s %s' % (gotmd5, targetfile) + if (md5 == gotmd5): + return targetfile + else: + print ' Expected: %s *MISMATCH*' % md5 + print '\n ** Corrupted ZIP? Delete %s to retry download.\n' % targetfile + return None icu_config = { 'variables': {} } @@ -723,7 +756,6 @@ def configure_intl(o): write(icu_config_name, do_not_edit + pprint.pformat(icu_config, indent=2) + '\n') - # small ICU is off by default. # always set icu_small, node.gyp depends on it being defined. o['variables']['icu_small'] = b(False) @@ -739,6 +771,8 @@ def configure_intl(o): o['variables']['icu_gyp_path'] = options.with_icu_path return # --with-intl= + if with_intl is None: + with_intl = 'none' # The default mode of Intl if with_intl == 'none' or with_intl is None: o['variables']['v8_enable_i18n_support'] = 0 return # no Intl @@ -769,20 +803,45 @@ def configure_intl(o): # Note: non-ICU implementations could use other 'with_intl' # values. + icu_parent_path = os.path.join(root_dir, 'deps') + icu_full_path = os.path.join(icu_parent_path, 'icu') + icu_small_path = os.path.join(icu_parent_path, 'icu-small') + icu_small_tag = os.path.join(icu_full_path, 'is-small-icu.txt') + + ## Use (or not) an embedded small-icu. + if with_intl == 'small-icu': + if not os.path.isdir(icu_full_path) and os.path.isdir(icu_small_path): + # deps/small-icu -> deps/icu + print 'Copying small ICU %s to %s' % (icu_small_path, icu_full_path) + shutil.copytree(icu_small_path, icu_full_path) + #else: + # print 'Not copying %s to %s' % (icu_small_path, icu_full_path) + elif os.path.isfile(icu_small_tag): + print 'deleting small-icu %s for --with-intl=%s' % (icu_full_path, with_intl) + shutil.rmtree(icu_full_path) + # ICU mode. (icu-generic.gyp) byteorder = sys.byteorder o['variables']['icu_gyp_path'] = 'tools/icu/icu-generic.gyp' # ICU source dir relative to root - icu_full_path = os.path.join(root_dir, 'deps/icu') o['variables']['icu_path'] = icu_full_path if not os.path.isdir(icu_full_path): - print 'Error: ICU path is not a directory: %s' % (icu_full_path) + print '* ECMA-402 (Intl) support didn\'t find ICU in %s..' % (icu_full_path) + # can we download (or find) a zipfile? + localzip = icu_download(icu_full_path) + if localzip: + nodedownload.unpack(localzip, icu_parent_path) + if not os.path.isdir(icu_full_path): + print ' Cannot build Intl without ICU in %s.' % (icu_full_path) + print ' (Fix, or disable with "--with-intl=none" )' sys.exit(1) + else: + print '* Using ICU in %s' % (icu_full_path) # Now, what version of ICU is it? We just need the "major", such as 54. # uvernum.h contains it as a #define. uvernum_h = os.path.join(icu_full_path, 'source/common/unicode/uvernum.h') if not os.path.isfile(uvernum_h): - print 'Error: could not load %s - is ICU installed?' % uvernum_h + print ' Error: could not load %s - is ICU installed?' % uvernum_h sys.exit(1) icu_ver_major = None matchVerExp = r'^\s*#define\s+U_ICU_VERSION_SHORT\s+"([^"]*)".*' @@ -792,7 +851,7 @@ def configure_intl(o): if m: icu_ver_major = m.group(1) if not icu_ver_major: - print 'Could not read U_ICU_VERSION_SHORT version from %s' % uvernum_h + print ' Could not read U_ICU_VERSION_SHORT version from %s' % uvernum_h sys.exit(1) icu_endianness = sys.byteorder[0]; # TODO(srl295): EBCDIC should be 'e' o['variables']['icu_ver_major'] = icu_ver_major @@ -819,8 +878,8 @@ def configure_intl(o): # this is the icudt*.dat file which node will be using (platform endianness) o['variables']['icu_data_file'] = icu_data_file if not os.path.isfile(icu_data_path): - print 'Error: ICU prebuilt data file %s does not exist.' % icu_data_path - print 'See the README.md.' + print ' Error: ICU prebuilt data file %s does not exist.' % icu_data_path + print ' See the README.md.' # .. and we're not about to build it from .gyp! sys.exit(1) # map from variable name to subdirs diff --git a/test/simple/test-intl.js b/test/simple/test-intl.js new file mode 100644 index 00000000000..1215641d5eb --- /dev/null +++ b/test/simple/test-intl.js @@ -0,0 +1,62 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var common = require('../common'); +var assert = require('assert'); + +var enablei18n = process.config.variables.v8_enable_i18n_support; +if (enablei18n === undefined) { + enablei18n = false; +} + +var haveIntl = ( global.Intl != undefined ); + +if (!haveIntl) { + assert.equal(enablei18n, false, '"Intl" object is NOT present but v8_enable_i18n_support is ' + enablei18n); + console.log('Skipping Intl tests because Intl object not present.'); +} else { + assert.equal(enablei18n, true, '"Intl" object is present but v8_enable_i18n_support is ' + enablei18n + '. Is this test out of date?'); + + // Check with toLocaleString + var date0 = new Date(0); + var GMT = 'Etc/GMT'; + var optsGMT = {timeZone: GMT}; + var localeString0 = date0.toLocaleString(['en'], optsGMT); + var expectString0 = '1/1/1970, 12:00:00 AM'; // epoch + assert.equal(localeString0, expectString0); + + // check with a Formatter + var dtf = new Intl.DateTimeFormat(['en'], {timeZone: GMT, month: 'short', year: '2-digit'}); + var localeString1 = dtf.format(date0); + assert.equal(localeString1, 'Jan 70'); + + // number format + assert.equal(new Intl.NumberFormat(['en']).format(12345.67890), '12,345.679'); + + var coll = new Intl.Collator(['en'],{sensitivity:'base',ignorePunctuation:true}); + + assert.equal(coll.compare('blackbird', 'black-bird'), 0, 'ignore punctuation failed'); + + assert.equal(coll.compare('blackbird', 'red-bird'), -1, 'compare less failed'); + assert.equal(coll.compare('bluebird', 'blackbird'), 1, 'compare greater failed'); + assert.equal(coll.compare('Bluebird', 'bluebird'), 0, 'ignore case failed'); + assert.equal(coll.compare('\ufb03', 'ffi'), 0, 'ffi ligature (contraction) failed'); +} diff --git a/tools/configure.d/nodedownload.py b/tools/configure.d/nodedownload.py new file mode 100644 index 00000000000..18602c8455b --- /dev/null +++ b/tools/configure.d/nodedownload.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# Moved some utilities here from ../../configure + +import urllib +import hashlib +import sys +import zipfile + +def formatSize(amt): + """Format a size as a string in MB""" + return "{:.1f}".format(amt / 1024000.) + +def spin(c): + """print out a spinner based on 'c'""" +# spin = "\\|/-" + spin = ".:|'" + return (spin[c % len(spin)]) + +class ConfigOpener(urllib.FancyURLopener): + """fancy opener used by retrievefile. Set a UA""" + # append to existing version (UA) + version = '%s node.js/configure' % urllib.URLopener.version + +def reporthook(count, size, total): + """internal hook used by retrievefile""" + sys.stdout.write(' Fetch: %c %sMB total, %sMB downloaded \r' % + (spin(count), + formatSize(total), + formatSize(count*size))) + +def retrievefile(url, targetfile): + """fetch file 'url' as 'targetfile'. Return targetfile or throw.""" + try: + sys.stdout.write(' <%s>\nConnecting...\r' % url) + sys.stdout.flush() + msg = ConfigOpener().retrieve(url, targetfile, reporthook=reporthook) + print '' # clear the line + return targetfile + except: + print ' ** Error occurred while downloading\n <%s>' % url + raise + +def md5sum(targetfile): + """md5sum a file. Return the hex digest.""" + digest = hashlib.md5() + with open(targetfile, 'rb') as f: + chunk = f.read(1024) + while chunk != "": + digest.update(chunk) + chunk = f.read(1024) + return digest.hexdigest() + +def unpack(packedfile, parent_path): + """Unpack packedfile into parent_path. Assumes .zip.""" + with zipfile.ZipFile(packedfile, 'r') as icuzip: + print ' Extracting source zip: %s' % packedfile + icuzip.extractall(parent_path) diff --git a/tools/icu/icu-generic.gyp b/tools/icu/icu-generic.gyp index 220d2c16a66..419efb525fb 100644 --- a/tools/icu/icu-generic.gyp +++ b/tools/icu/icu-generic.gyp @@ -11,6 +11,17 @@ }, 'includes': [ '../../icu_config.gypi' ], 'targets': [ + { + # a target for additional uconfig defines, target only + 'target_name': 'icu_uconfig_target', + 'type': 'none', + 'toolsets': [ 'target' ], + 'direct_dependent_settings': { + 'defines': [ + 'UCONFIG_NO_CONVERSION=1', + ] + }, + }, { # a target to hold uconfig defines. # for now these are hard coded, but could be defined. @@ -97,19 +108,67 @@ 'sources': [ '<@(icu_src_i18n)' ], + 'conditions': [ + [ 'icu_ver_major == 54', { 'sources!': [ + ## Strip out the following for ICU 54 only. + ## add more conditions in the future? + ## if your compiler can dead-strip, this will + ## make ZERO difference to binary size. + ## Made ICU-specific for future-proofing. + + # alphabetic index + '../../deps/icu/source/i18n/alphaindex.cpp', + # BOCSU + # misc + '../../deps/icu/source/i18n/dtitvfmt.cpp', + '../../deps/icu/source/i18n/dtitvinf.cpp', + '../../deps/icu/source/i18n/dtitv_impl.h', + '../../deps/icu/source/i18n/quantityformatter.cpp', + '../../deps/icu/source/i18n/quantityformatter.h', + '../../deps/icu/source/i18n/regexcmp.cpp', + '../../deps/icu/source/i18n/regexcmp.h', + '../../deps/icu/source/i18n/regexcst.h', + '../../deps/icu/source/i18n/regeximp.cpp', + '../../deps/icu/source/i18n/regeximp.h', + '../../deps/icu/source/i18n/regexst.cpp', + '../../deps/icu/source/i18n/regexst.h', + '../../deps/icu/source/i18n/regextxt.cpp', + '../../deps/icu/source/i18n/regextxt.h', + '../../deps/icu/source/i18n/region.cpp', + '../../deps/icu/source/i18n/region_impl.h', + '../../deps/icu/source/i18n/reldatefmt.cpp', + '../../deps/icu/source/i18n/reldatefmt.h', + '../../deps/icu/source/i18n/measfmt.h', + '../../deps/icu/source/i18n/measfmt.cpp', + '../../deps/icu/source/i18n/scientificformathelper.cpp', + '../../deps/icu/source/i18n/tmunit.cpp', + '../../deps/icu/source/i18n/tmutamt.cpp', + '../../deps/icu/source/i18n/tmutfmt.cpp', + '../../deps/icu/source/i18n/uregex.cpp', + '../../deps/icu/source/i18n/uregexc.cpp', + '../../deps/icu/source/i18n/uregion.cpp', + '../../deps/icu/source/i18n/uspoof.cpp', + '../../deps/icu/source/i18n/uspoof_build.cpp', + '../../deps/icu/source/i18n/uspoof_conf.cpp', + '../../deps/icu/source/i18n/uspoof_conf.h', + '../../deps/icu/source/i18n/uspoof_impl.cpp', + '../../deps/icu/source/i18n/uspoof_impl.h', + '../../deps/icu/source/i18n/uspoof_wsconf.cpp', + '../../deps/icu/source/i18n/uspoof_wsconf.h', + ]}]], 'include_dirs': [ '../../deps/icu/source/i18n', ], 'defines': [ 'U_I18N_IMPLEMENTATION=1', ], - 'dependencies': [ 'icuucx', 'icu_implementation', 'icu_uconfig' ], + 'dependencies': [ 'icuucx', 'icu_implementation', 'icu_uconfig', 'icu_uconfig_target' ], 'direct_dependent_settings': { 'include_dirs': [ '../../deps/icu/source/i18n', ], }, - 'export_dependent_settings': [ 'icuucx' ], + 'export_dependent_settings': [ 'icuucx', 'icu_uconfig_target' ], }, # This exports actual ICU data { @@ -289,22 +348,42 @@ 'export_dependent_settings': [ 'icuucx', 'icudata' ], }, # This is the 'real' icuuc. - # tools can depend on 'icuuc + stubdata' { 'target_name': 'icuucx', 'type': '<(library)', - 'dependencies': [ 'icu_implementation', 'icu_uconfig' ], + 'dependencies': [ 'icu_implementation', 'icu_uconfig', 'icu_uconfig_target' ], 'toolsets': [ 'target' ], 'sources': [ - '<@(icu_src_common)' + '<@(icu_src_common)', ], + 'conditions': [ + [ 'icu_ver_major == 54', { 'sources!': [ + ## Strip out the following for ICU 54 only. + ## add more conditions in the future? + ## if your compiler can dead-strip, this will + ## make ZERO difference to binary size. + ## Made ICU-specific for future-proofing. + + # bidi- not needed (yet!) + '../../deps/icu/source/common/ubidi.c', + '../../deps/icu/source/common/ubidiimp.h', + '../../deps/icu/source/common/ubidiln.c', + '../../deps/icu/source/common/ubidiwrt.c', + #'../../deps/icu/source/common/ubidi_props.c', + #'../../deps/icu/source/common/ubidi_props.h', + #'../../deps/icu/source/common/ubidi_props_data.h', + # and the callers + '../../deps/icu/source/common/ushape.cpp', + '../../deps/icu/source/common/usprep.cpp', + '../../deps/icu/source/common/uts46.cpp', + ]}]], 'include_dirs': [ '../../deps/icu/source/common', ], 'defines': [ 'U_COMMON_IMPLEMENTATION=1', ], - 'export_dependent_settings': [ 'icu_uconfig' ], + 'export_dependent_settings': [ 'icu_uconfig', 'icu_uconfig_target' ], 'direct_dependent_settings': { 'include_dirs': [ '../../deps/icu/source/common', diff --git a/tools/icu/icu_small.json b/tools/icu/icu_small.json index ddf7d1204e8..038d399d3e8 100644 --- a/tools/icu/icu_small.json +++ b/tools/icu/icu_small.json @@ -28,7 +28,8 @@ "translit": "none", "brkfiles": "none", "brkdict": "none", - "confusables": "none" + "confusables": "none", + "unit": "none" }, "remove": [ "cnvalias.icu", diff --git a/tools/icu/prepare-icu-source.sh b/tools/icu/prepare-icu-source.sh new file mode 100644 index 00000000000..7b38eed49a8 --- /dev/null +++ b/tools/icu/prepare-icu-source.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# +# Call this to regenerate /deps/icu-small +# + +if [ -d `dirname "$0"` ]; +then + cd `dirname "$0"` +fi + +# now we are in /tools/icu +cd ../.. +# now we are in , hopefully +if [ ! -d 'tools/icu/' ]; +then + echo "$0: error: cannot find tools/icu in" `pwd` + exit 1 +fi + +echo "Preparing ICU source in" `pwd` +if [ -d 'deps/icu/' ]; +then + echo "Deleting" 'deps/icu' + rm -rf deps/icu || exit 1 +fi + +# clean +echo 'Delete ./out' +rm -rf ./out +# delete old ICU tgz +echo 'Delete deps/icu-small' +rm -rf ./deps/icu-small + +echo "Configuring node.. hopefully with ninja" +./configure --with-intl=small-icu --ninja || ./configure --with-intl=small-icu || exit 1 +echo "Building node.." +make || ( ./configure --with-intl=small-icu && make ) || exit 1 + +echo "Leaving our mark" +echo "# Generated. This is the SMALL ICU dir. Don't commit changes against it." > deps/icu/is-small-icu.txt +echo "Great. trimming stuff.." +rm -rfv deps/icu/[Aap]* +rm -rfv deps/icu/source/allinone deps/icu/source/config deps/icu/source/test +rm -rfv deps/icu/source/layout* deps/icu/source/samples deps/icu/source/tools/ctestfw +rm -rfv deps/icu/source/tools/gen[bdns]* deps/icu/source/tools/icuinfo +rm -rfv deps/icu/source/tools/m* deps/icu/source/tools/tzcode +rm -rfv deps/icu/source/data/{curr,lang,misc,region,sprep,unidata,unit,zone} +find deps/icu \( -name 'Makefile*' -o -name '*\.vcx*' \) -print0 | xargs -0 rm -fv +# now, the harsh part. Prove you are compiled! +sh tools/icu/trim-uncompiled-source.sh + +# move data +cp ./out/Release/gen/icutmp/icudt*l.dat deps/icu/source/data/in/ + +mv -v deps/icu deps/icu-small + +du -sh deps/icu-small diff --git a/tools/icu/trim-uncompiled-source.sh b/tools/icu/trim-uncompiled-source.sh new file mode 100644 index 00000000000..be6214b52fa --- /dev/null +++ b/tools/icu/trim-uncompiled-source.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# +# Don't call this directly. Called by prepare-icu-source.sh +# + +#./out/Release/obj/deps/icu/source/i18n/icui18n.collationsets.o +#./out/Release/obj/deps/icu/source/i18n/icutools.collationsets.o +for file in `find deps/icu/source -name '*.c' -o -name '*.cpp'`; +do + #echo "# ${file}" + base=`basename ${file} .c` + base=`basename ${base} .cpp` + dir=`dirname ${file}` + #echo ${dir}/${base} + if ls "out/Release/obj/${dir}/"*".${base}.o" 2>/dev/null >/dev/null; + then + true + else + rm -fv ${file} + fi +done diff --git a/vcbuild.bat b/vcbuild.bat index 616b5bb1145..02f97fb7c7e 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -65,6 +65,7 @@ if /i "%1"=="upload" set upload=1&goto arg-ok if /i "%1"=="jslint" set jslint=1&goto arg-ok if /i "%1"=="small-icu" set i18n_arg=%1&goto arg-ok if /i "%1"=="full-icu" set i18n_arg=%1&goto arg-ok +if /i "%1"=="intl-none" set i18n_arg=%1&goto arg-ok echo Warning: ignoring invalid command line option `%1`. @@ -85,6 +86,7 @@ if defined noperfctr set noperfctr_arg=--without-perfctr& set noperfctr_msi_arg= if "%i18n_arg%"=="full-icu" set i18n_arg=--with-intl=full-icu if "%i18n_arg%"=="small-icu" set i18n_arg=--with-intl=small-icu +if "%i18n_arg%"=="intl-none" set i18n_arg=--with-intl=none :project-gen @rem Skip project generation if requested. @@ -232,7 +234,7 @@ python tools/closure_linter/closure_linter/gjslint.py --unix_mode --strict --noj goto exit :help -echo vcbuild.bat [debug/release] [msi] [test-all/test-uv/test-internet/test-pummel/test-simple/test-message] [clean] [noprojgen] [nobuild] [nosign] [x86/x64] +echo vcbuild.bat [debug/release] [msi] [test-all/test-uv/test-internet/test-pummel/test-simple/test-message] [clean] [noprojgen] [small-icu/full-icu/intl-none] [nobuild] [nosign] [x86/x64] echo Examples: echo vcbuild.bat : builds release build echo vcbuild.bat debug : builds debug build From c1e66b300d24af67f84495ae1ebad4b2680d82f9 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Wed, 26 Nov 2014 17:30:30 -0800 Subject: [PATCH 02/10] build: i18n: add `configure --with-icu-source=...` Usage: * `--with-icu-source=/path/to/my/other/icu` * `--with-icu-source=/path/to/icu54.zip` * `--with-icu-source=/path/to/icu54.tgz` * `--with-icu-source=http://example.com/icu54.tar.bz2` this fixes srl295/node#11 --- .gitignore | 1 + README.md | 24 +++++++++++---- configure | 49 +++++++++++++++++++++++++++++-- tools/configure.d/nodedownload.py | 24 ++++++++++++--- 4 files changed, 85 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 39775b1767d..9dda73b2e4d 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,7 @@ deps/v8-* deps/icu deps/icu*.zip deps/icu*.tgz +deps/icu-tmp ./node_modules .svn/ diff --git a/README.md b/README.md index 88588ecbf52..bb31c53c018 100644 --- a/README.md +++ b/README.md @@ -152,17 +152,29 @@ pkg-config --modversion icu-i18n && ./configure --with-intl=system-icu #### Build with a specific ICU: -First: Unpack latest ICU to `deps/icu` - [icu4c-**##.#**-src.tgz](http://icu-project.org/download) (or `.zip`) - as `deps/icu` (You'll have: `deps/icu/source/...`) +You can find other ICU releases at [icu4c-**##.#**-src.tgz](http://icu-project.org/download) (or `.zip`) -Unix/Macintosh: +Unix/Macintosh: from an already-unpacked ICU ```sh -./configure --with-intl=full-icu +./configure --with-intl=full-icu --with-icu-source=/path/to/icu ``` -Windows: +Unix/Macintosh: from a local ICU tarball + +```sh +./configure --with-intl=full-icu --with-icu-source=/path/to/icu.tgz +``` + +Unix/Macintosh: from a tarball URL + +```sh +./configure --with-intl=full-icu --with-icu-source=http://url/to/icu.tgz +``` + +Windows: first unpack latest ICU to `deps/icu` + [icu4c-**##.#**-src.tgz](http://icu-project.org/download) (or `.zip`) + as `deps/icu` (You'll have: `deps/icu/source/...`) ```sh vcbuild full-icu diff --git a/configure b/configure index 37d8a4b6804..d172bca5e0b 100755 --- a/configure +++ b/configure @@ -251,6 +251,11 @@ parser.add_option('--with-intl', dest='with_intl', help='Intl mode: none, full-icu, small-icu (default is none)') +parser.add_option('--with-icu-source', + action='store', + dest='with_icu_source', + help='Intl mode: optional local path to icu/ dir, or path/URL of icu source archive.') + parser.add_option('--with-perfctr', action='store_true', dest='with_perfctr', @@ -760,6 +765,7 @@ def configure_intl(o): o['variables']['icu_small'] = b(False) with_intl = options.with_intl + with_icu_source = options.with_icu_source have_icu_path = bool(options.with_icu_path) if have_icu_path and with_intl: print 'Error: Cannot specify both --with-icu-path and --with-intl' @@ -807,9 +813,45 @@ def configure_intl(o): icu_full_path = os.path.join(icu_parent_path, 'icu') icu_small_path = os.path.join(icu_parent_path, 'icu-small') icu_small_tag = os.path.join(icu_full_path, 'is-small-icu.txt') - - ## Use (or not) an embedded small-icu. - if with_intl == 'small-icu': + icu_tmp_path = os.path.join(icu_parent_path, 'icu-tmp') + + ## Has the user specified an ICU source path? + if with_icu_source: + if os.path.isdir(icu_full_path): + print 'Deleting old ICU source: %s' % (icu_full_path) + shutil.rmtree(icu_full_path) + # now, what path was given? + if os.path.isdir(with_icu_source): + # it's a path. Copy it. + print '%s -> %s' % (with_icu_source, icu_full_path) + shutil.copytree(with_icu_source, icu_full_path) + else: + # could be file or URL. Set up temporary area + if os.path.isdir(icu_tmp_path): + shutil.rmtree(icu_tmp_path) + os.mkdir(icu_tmp_path) + icu_tarball = None + if os.path.isfile(with_icu_source): + # it's a file. Try to unpack it. + icu_tarball = with_icu_source + else: + # Can we download it? + local = os.path.join(icu_tmp_path, with_icu_source.split('/')[-1]) # local part + icu_tarball = nodedownload.retrievefile(with_icu_source, local) + # continue with "icu_tarball" + nodedownload.unpack(icu_tarball, icu_tmp_path) + # Did it unpack correctly? Should contain 'icu' + tmp_icu = os.path.join(icu_tmp_path, 'icu') + if os.path.isdir(tmp_icu): + os.rename(tmp_icu, icu_full_path) + shutil.rmtree(icu_tmp_path) + else: + print ' Error: --with-icu-source=%s did not result in an "icu" dir.' % with_icu_source + shutil.rmtree(icu_tmp_path) + sys.exit(1) + + elif with_intl == 'small-icu': + ## Use (or not) an embedded small-icu. if not os.path.isdir(icu_full_path) and os.path.isdir(icu_small_path): # deps/small-icu -> deps/icu print 'Copying small ICU %s to %s' % (icu_small_path, icu_full_path) @@ -817,6 +859,7 @@ def configure_intl(o): #else: # print 'Not copying %s to %s' % (icu_small_path, icu_full_path) elif os.path.isfile(icu_small_tag): + ## Not small ICU nor custom path. Delete the old deps/icu print 'deleting small-icu %s for --with-intl=%s' % (icu_full_path, with_intl) shutil.rmtree(icu_full_path) diff --git a/tools/configure.d/nodedownload.py b/tools/configure.d/nodedownload.py index 18602c8455b..2efa509a208 100644 --- a/tools/configure.d/nodedownload.py +++ b/tools/configure.d/nodedownload.py @@ -5,6 +5,7 @@ import hashlib import sys import zipfile +import tarfile def formatSize(amt): """Format a size as a string in MB""" @@ -50,8 +51,23 @@ def md5sum(targetfile): chunk = f.read(1024) return digest.hexdigest() -def unpack(packedfile, parent_path): - """Unpack packedfile into parent_path. Assumes .zip.""" - with zipfile.ZipFile(packedfile, 'r') as icuzip: - print ' Extracting source zip: %s' % packedfile +def unpackWithMode(opener, packedfile, parent_path, mode): + with opener(packedfile, mode) as icuzip: + print ' Extracting source file: %s' % packedfile icuzip.extractall(parent_path) + +def unpack(packedfile, parent_path): + """Unpack packedfile into parent_path. Assumes .zip. Returns parent_path""" + packedsuffix = packedfile.lower().split('.')[-1] # .zip, .tgz etc + if zipfile.is_zipfile(packedfile): + with zipfile.ZipFile(packedfile, 'r') as icuzip: + print ' Extracting zipfile: %s' % packedfile + icuzip.extractall(parent_path) + return parent_path + elif tarfile.is_tarfile(packedfile): + with tarfile.TarFile.open(packedfile, 'r') as icuzip: + print ' Extracting tarfile: %s' % packedfile + icuzip.extractall(parent_path) + return parent_path + else: + raise Exception('Error: Don\'t know how to unpack %s with extension %s' % (packedfile, packedsuffix)) From 7dc107cb25a83c56a919712d2f159914080db000 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Tue, 9 Dec 2014 18:06:07 -0800 Subject: [PATCH 03/10] build: i18n: add --with-icu-locales option this lets you choose which locales are used in the `small-icu` case Example: configure --with-intl=small-icu --with-icu-locales=tlh,grc,nl Note that the unit test tests/simple/test-intl.js hasn't been updated yet. (Also note that as of this writing, neither Klingon nor Ancient Greek are in upstream CLDR data. Serving suggestion only.) This completely and totally fixes #10 :octocat: --- configure | 17 ++++++++++++++ test/simple/test-intl.js | 49 ++++++++++++++++++++++++++------------- tools/icu/icu-generic.gyp | 6 +++-- tools/icu/icu_small.json | 12 +++++----- tools/icu/icutrim.py | 13 +++++++++++ 5 files changed, 73 insertions(+), 24 deletions(-) diff --git a/configure b/configure index d172bca5e0b..f56b9bc55ae 100755 --- a/configure +++ b/configure @@ -7,6 +7,7 @@ import shlex import subprocess import sys import shutil +import string CC = os.environ.get('CC', 'cc') @@ -246,6 +247,11 @@ parser.add_option('--with-icu-path', dest='with_icu_path', help='Path to icu.gyp (ICU i18n, Chromium version only.)') +parser.add_option('--with-icu-locales', + action='store', + dest='with_icu_locales', + help='Comma-separated list of locales for "small-icu". Default: "root,en". "root" is assumed.') + parser.add_option('--with-intl', action='store', dest='with_intl', @@ -777,8 +783,13 @@ def configure_intl(o): o['variables']['icu_gyp_path'] = options.with_icu_path return # --with-intl= + # set the default if with_intl is None: with_intl = 'none' # The default mode of Intl + # sanity check localelist + if options.with_icu_locales and (with_intl != 'small-icu'): + print 'Error: --with-icu-locales only makes sense with --with-intl=small-icu' + sys.exit(1) if with_intl == 'none' or with_intl is None: o['variables']['v8_enable_i18n_support'] = 0 return # no Intl @@ -786,6 +797,12 @@ def configure_intl(o): # small ICU (English only) o['variables']['v8_enable_i18n_support'] = 1 o['variables']['icu_small'] = b(True) + with_icu_locales = options.with_icu_locales + if not with_icu_locales: + with_icu_locales = 'root,en' + locs = set(with_icu_locales.split(',')) + locs.add('root') # must have root + o['variables']['icu_locales'] = string.join(locs,',') elif with_intl == 'full-icu': # full ICU o['variables']['v8_enable_i18n_support'] = 1 diff --git a/test/simple/test-intl.js b/test/simple/test-intl.js index 1215641d5eb..a7618bc7c84 100644 --- a/test/simple/test-intl.js +++ b/test/simple/test-intl.js @@ -29,34 +29,51 @@ if (enablei18n === undefined) { var haveIntl = ( global.Intl != undefined ); +function haveLocale(loc) { + var locs = process.config.variables.icu_locales.split(','); + if ( locs.indexOf(loc) !== -1 ) { + return true; + } else { + return false; + } +} + if (!haveIntl) { assert.equal(enablei18n, false, '"Intl" object is NOT present but v8_enable_i18n_support is ' + enablei18n); console.log('Skipping Intl tests because Intl object not present.'); } else { assert.equal(enablei18n, true, '"Intl" object is present but v8_enable_i18n_support is ' + enablei18n + '. Is this test out of date?'); - // Check with toLocaleString + // check with a Formatter var date0 = new Date(0); var GMT = 'Etc/GMT'; var optsGMT = {timeZone: GMT}; - var localeString0 = date0.toLocaleString(['en'], optsGMT); var expectString0 = '1/1/1970, 12:00:00 AM'; // epoch - assert.equal(localeString0, expectString0); - // check with a Formatter var dtf = new Intl.DateTimeFormat(['en'], {timeZone: GMT, month: 'short', year: '2-digit'}); - var localeString1 = dtf.format(date0); - assert.equal(localeString1, 'Jan 70'); - - // number format - assert.equal(new Intl.NumberFormat(['en']).format(12345.67890), '12,345.679'); - var coll = new Intl.Collator(['en'],{sensitivity:'base',ignorePunctuation:true}); - - assert.equal(coll.compare('blackbird', 'black-bird'), 0, 'ignore punctuation failed'); + if ( !process.config.variables.icu_locales // if no list specified or.. + || haveLocale('en') ) { // list contains 'en' then continue - assert.equal(coll.compare('blackbird', 'red-bird'), -1, 'compare less failed'); - assert.equal(coll.compare('bluebird', 'blackbird'), 1, 'compare greater failed'); - assert.equal(coll.compare('Bluebird', 'bluebird'), 0, 'ignore case failed'); - assert.equal(coll.compare('\ufb03', 'ffi'), 0, 'ffi ligature (contraction) failed'); + // Check with toLocaleString + var localeString1 = dtf.format(date0); + assert.equal(localeString1, 'Jan 70'); + + var localeString0 = date0.toLocaleString(['en'], optsGMT); + assert.equal(localeString0, expectString0); + + // number format + assert.equal(new Intl.NumberFormat(['en']).format(12345.67890), '12,345.679'); + + var coll = new Intl.Collator(['en'],{sensitivity:'base',ignorePunctuation:true}); + + assert.equal(coll.compare('blackbird', 'black-bird'), 0, 'ignore punctuation failed'); + + assert.equal(coll.compare('blackbird', 'red-bird'), -1, 'compare less failed'); + assert.equal(coll.compare('bluebird', 'blackbird'), 1, 'compare greater failed'); + assert.equal(coll.compare('Bluebird', 'bluebird'), 0, 'ignore case failed'); + assert.equal(coll.compare('\ufb03', 'ffi'), 0, 'ffi ligature (contraction) failed'); + } else { + console.log('Skipping detailed Intl tests because English is not listed as supported.'); + } } diff --git a/tools/icu/icu-generic.gyp b/tools/icu/icu-generic.gyp index 419efb525fb..ef63cd33fab 100644 --- a/tools/icu/icu-generic.gyp +++ b/tools/icu/icu-generic.gyp @@ -214,7 +214,8 @@ '-T', '../../out/icutmp', '-F', 'icu_small.json', '-O', 'icudt<(icu_ver_major)<(icu_endianness).dat', - '-v' ], + '-v', + '-L', '<(icu_locales)'], }, { # build final .dat -> .obj @@ -294,7 +295,8 @@ '-T', '<(SHARED_INTERMEDIATE_DIR)/icutmp', '-F', 'icu_small.json', '-O', 'icudt<(icu_ver_major)<(icu_endianness).dat', - '-v' ], + '-v', + '-L', '<(icu_locales)'], }, { # rename to get the final entrypoint name right 'action_name': 'rename', diff --git a/tools/icu/icu_small.json b/tools/icu/icu_small.json index 038d399d3e8..e434794e91c 100644 --- a/tools/icu/icu_small.json +++ b/tools/icu/icu_small.json @@ -1,11 +1,11 @@ { "copyright": "Copyright (c) 2014 IBM Corporation and Others. All Rights Reserved.", - "comment": "icutrim.py config: Trim down ICU to just English, needed for node.js use.", + "comment": "icutrim.py config: Trim down ICU to just a certain locale set, needed for node.js use.", "variables": { "none": { "only": [] }, - "en_only": { + "locales": { "only": [ "root", "en" @@ -15,14 +15,14 @@ } }, "trees": { - "ROOT": "en_only", + "ROOT": "locales", "brkitr": "none", - "coll": "en_only", - "curr": "en_only", + "coll": "locales", + "curr": "locales", "lang": "none", "rbnf": "none", "region": "none", - "zone": "en_only", + "zone": "locales", "converters": "none", "stringprep": "none", "translit": "none", diff --git a/tools/icu/icutrim.py b/tools/icu/icutrim.py index 7f0fb3752e4..517bf39bad3 100755 --- a/tools/icu/icutrim.py +++ b/tools/icu/icutrim.py @@ -65,6 +65,12 @@ action="count", default=0) +parser.add_option('-L',"--locales", + action="store", + dest="locales", + help="sets the 'locales.only' variable", + default=None) + parser.add_option('-e', '--endian', action='store', dest='endian', help='endian, big, little or host, your default is "%s".' % endian, default=endian, metavar='endianness') (options, args) = parser.parse_args() @@ -147,6 +153,13 @@ def runcmd(tool, cmd, doContinue=False): config=json.load(fi) fi.close() +if (options.locales): + if not config.has_key("variables"): + config["variables"] = {} + if not config["variables"].has_key("locales"): + config["variables"]["locales"] = {} + config["variables"]["locales"]["only"] = options.locales.split(',') + if (options.verbose > 6): print config From b04809223dccb4e1023c5992943141bf565a0ced Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Tue, 9 Dec 2014 14:14:49 -0800 Subject: [PATCH 04/10] build: i18n: fix target builds With dependency fix as suggested by @misterdjules fixes srl295/node#12 --- tools/icu/icu-generic.gyp | 155 +++++++++++++++++++++----------------- 1 file changed, 86 insertions(+), 69 deletions(-) diff --git a/tools/icu/icu-generic.gyp b/tools/icu/icu-generic.gyp index ef63cd33fab..2e9376f7e0a 100644 --- a/tools/icu/icu-generic.gyp +++ b/tools/icu/icu-generic.gyp @@ -103,72 +103,81 @@ }, { 'target_name': 'icui18n', - 'type': '<(library)', - 'toolsets': [ 'target' ], - 'sources': [ - '<@(icu_src_i18n)' - ], - 'conditions': [ - [ 'icu_ver_major == 54', { 'sources!': [ - ## Strip out the following for ICU 54 only. - ## add more conditions in the future? - ## if your compiler can dead-strip, this will - ## make ZERO difference to binary size. - ## Made ICU-specific for future-proofing. - - # alphabetic index - '../../deps/icu/source/i18n/alphaindex.cpp', - # BOCSU - # misc - '../../deps/icu/source/i18n/dtitvfmt.cpp', - '../../deps/icu/source/i18n/dtitvinf.cpp', - '../../deps/icu/source/i18n/dtitv_impl.h', - '../../deps/icu/source/i18n/quantityformatter.cpp', - '../../deps/icu/source/i18n/quantityformatter.h', - '../../deps/icu/source/i18n/regexcmp.cpp', - '../../deps/icu/source/i18n/regexcmp.h', - '../../deps/icu/source/i18n/regexcst.h', - '../../deps/icu/source/i18n/regeximp.cpp', - '../../deps/icu/source/i18n/regeximp.h', - '../../deps/icu/source/i18n/regexst.cpp', - '../../deps/icu/source/i18n/regexst.h', - '../../deps/icu/source/i18n/regextxt.cpp', - '../../deps/icu/source/i18n/regextxt.h', - '../../deps/icu/source/i18n/region.cpp', - '../../deps/icu/source/i18n/region_impl.h', - '../../deps/icu/source/i18n/reldatefmt.cpp', - '../../deps/icu/source/i18n/reldatefmt.h', - '../../deps/icu/source/i18n/measfmt.h', - '../../deps/icu/source/i18n/measfmt.cpp', - '../../deps/icu/source/i18n/scientificformathelper.cpp', - '../../deps/icu/source/i18n/tmunit.cpp', - '../../deps/icu/source/i18n/tmutamt.cpp', - '../../deps/icu/source/i18n/tmutfmt.cpp', - '../../deps/icu/source/i18n/uregex.cpp', - '../../deps/icu/source/i18n/uregexc.cpp', - '../../deps/icu/source/i18n/uregion.cpp', - '../../deps/icu/source/i18n/uspoof.cpp', - '../../deps/icu/source/i18n/uspoof_build.cpp', - '../../deps/icu/source/i18n/uspoof_conf.cpp', - '../../deps/icu/source/i18n/uspoof_conf.h', - '../../deps/icu/source/i18n/uspoof_impl.cpp', - '../../deps/icu/source/i18n/uspoof_impl.h', - '../../deps/icu/source/i18n/uspoof_wsconf.cpp', - '../../deps/icu/source/i18n/uspoof_wsconf.h', - ]}]], - 'include_dirs': [ - '../../deps/icu/source/i18n', - ], - 'defines': [ - 'U_I18N_IMPLEMENTATION=1', + 'toolsets': [ 'target', 'host' ], + 'conditions' : [ + ['_toolset=="target"', { + 'type': '<(library)', + 'sources': [ + '<@(icu_src_i18n)' + ], + 'conditions': [ + [ 'icu_ver_major == 54', { 'sources!': [ + ## Strip out the following for ICU 54 only. + ## add more conditions in the future? + ## if your compiler can dead-strip, this will + ## make ZERO difference to binary size. + ## Made ICU-specific for future-proofing. + + # alphabetic index + '../../deps/icu/source/i18n/alphaindex.cpp', + # BOCSU + # misc + '../../deps/icu/source/i18n/dtitvfmt.cpp', + '../../deps/icu/source/i18n/dtitvinf.cpp', + '../../deps/icu/source/i18n/dtitv_impl.h', + '../../deps/icu/source/i18n/quantityformatter.cpp', + '../../deps/icu/source/i18n/quantityformatter.h', + '../../deps/icu/source/i18n/regexcmp.cpp', + '../../deps/icu/source/i18n/regexcmp.h', + '../../deps/icu/source/i18n/regexcst.h', + '../../deps/icu/source/i18n/regeximp.cpp', + '../../deps/icu/source/i18n/regeximp.h', + '../../deps/icu/source/i18n/regexst.cpp', + '../../deps/icu/source/i18n/regexst.h', + '../../deps/icu/source/i18n/regextxt.cpp', + '../../deps/icu/source/i18n/regextxt.h', + '../../deps/icu/source/i18n/region.cpp', + '../../deps/icu/source/i18n/region_impl.h', + '../../deps/icu/source/i18n/reldatefmt.cpp', + '../../deps/icu/source/i18n/reldatefmt.h', + '../../deps/icu/source/i18n/measfmt.h', + '../../deps/icu/source/i18n/measfmt.cpp', + '../../deps/icu/source/i18n/scientificformathelper.cpp', + '../../deps/icu/source/i18n/tmunit.cpp', + '../../deps/icu/source/i18n/tmutamt.cpp', + '../../deps/icu/source/i18n/tmutfmt.cpp', + '../../deps/icu/source/i18n/uregex.cpp', + '../../deps/icu/source/i18n/uregexc.cpp', + '../../deps/icu/source/i18n/uregion.cpp', + '../../deps/icu/source/i18n/uspoof.cpp', + '../../deps/icu/source/i18n/uspoof_build.cpp', + '../../deps/icu/source/i18n/uspoof_conf.cpp', + '../../deps/icu/source/i18n/uspoof_conf.h', + '../../deps/icu/source/i18n/uspoof_impl.cpp', + '../../deps/icu/source/i18n/uspoof_impl.h', + '../../deps/icu/source/i18n/uspoof_wsconf.cpp', + '../../deps/icu/source/i18n/uspoof_wsconf.h', + ]}]], + 'include_dirs': [ + '../../deps/icu/source/i18n', + ], + 'defines': [ + 'U_I18N_IMPLEMENTATION=1', + ], + 'dependencies': [ 'icuucx', 'icu_implementation', 'icu_uconfig', 'icu_uconfig_target' ], + 'direct_dependent_settings': { + 'include_dirs': [ + '../../deps/icu/source/i18n', + ], + }, + 'export_dependent_settings': [ 'icuucx', 'icu_uconfig_target' ], + }], + ['_toolset=="host"', { + 'type': 'none', + 'dependencies': [ 'icutools' ], + 'export_dependent_settings': [ 'icutools' ], + }], ], - 'dependencies': [ 'icuucx', 'icu_implementation', 'icu_uconfig', 'icu_uconfig_target' ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../../deps/icu/source/i18n', - ], - }, - 'export_dependent_settings': [ 'icuucx', 'icu_uconfig_target' ], }, # This exports actual ICU data { @@ -345,9 +354,17 @@ { 'target_name': 'icuuc', 'type': 'none', - 'toolsets': [ 'target' ], - 'dependencies': [ 'icuucx', 'icudata' ], - 'export_dependent_settings': [ 'icuucx', 'icudata' ], + 'toolsets': [ 'target', 'host' ], + 'conditions' : [ + ['_toolset=="host"', { + 'dependencies': [ 'icutools' ], + 'export_dependent_settings': [ 'icutools' ], + }], + ['_toolset=="target"', { + 'dependencies': [ 'icuucx', 'icudata' ], + 'export_dependent_settings': [ 'icuucx', 'icudata' ], + }], + ], }, # This is the 'real' icuuc. { @@ -440,7 +457,7 @@ }], ], }, - 'export_dependent_settings': [ 'icu_implementation', 'icu_uconfig' ], + 'export_dependent_settings': [ 'icu_uconfig' ], }, # This tool is needed to rebuild .res files from .txt, # or to build index (res_index.txt) files for small-icu From 5891aad13d58a9ae1c5cf5fae252354319dfe044 Mon Sep 17 00:00:00 2001 From: Julien Gilli Date: Tue, 16 Dec 2014 17:47:10 -0800 Subject: [PATCH 05/10] build: make nodedownload.py work with Python 2.6 --- tools/configure.d/nodedownload.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/configure.d/nodedownload.py b/tools/configure.d/nodedownload.py index 2efa509a208..4d805ab9f8b 100644 --- a/tools/configure.d/nodedownload.py +++ b/tools/configure.d/nodedownload.py @@ -6,10 +6,12 @@ import sys import zipfile import tarfile +import fpformat +import contextlib def formatSize(amt): """Format a size as a string in MB""" - return "{:.1f}".format(amt / 1024000.) + return fpformat.fix(amt / 1024000., 1) def spin(c): """print out a spinner based on 'c'""" @@ -60,7 +62,7 @@ def unpack(packedfile, parent_path): """Unpack packedfile into parent_path. Assumes .zip. Returns parent_path""" packedsuffix = packedfile.lower().split('.')[-1] # .zip, .tgz etc if zipfile.is_zipfile(packedfile): - with zipfile.ZipFile(packedfile, 'r') as icuzip: + with contextlib.closing(zipfile.ZipFile(packedfile, 'r')) as icuzip: print ' Extracting zipfile: %s' % packedfile icuzip.extractall(parent_path) return parent_path From 8d042818226818c3ed7cc8d14538ebbaa9f2fe6e Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Tue, 16 Dec 2014 14:20:58 -0800 Subject: [PATCH 06/10] build: i18n: Don't use hard coded ../../out paths on windows This was suggested by @misterdjules as it causes test failures. With this fix, "out" is no longer created on windows and python tools/test.py simple .. can run properly. --- tools/icu/icu-generic.gyp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/icu/icu-generic.gyp b/tools/icu/icu-generic.gyp index 2e9376f7e0a..896acc697d1 100644 --- a/tools/icu/icu-generic.gyp +++ b/tools/icu/icu-generic.gyp @@ -214,13 +214,13 @@ # trim down ICU 'action_name': 'icutrim', 'inputs': [ '<(icu_data_in)', 'icu_small.json' ], - 'outputs': [ '../../out/icutmp/icudt<(icu_ver_major)<(icu_endianness).dat' ], + 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/icutmp/icudt<(icu_ver_major)<(icu_endianness).dat' ], 'action': [ 'python', 'icutrim.py', '-P', '../../<(CONFIGURATION_NAME)', '-D', '<(icu_data_in)', '--delete-tmp', - '-T', '../../out/icutmp', + '-T', '<(SHARED_INTERMEDIATE_DIR)/icutmp', '-F', 'icu_small.json', '-O', 'icudt<(icu_ver_major)<(icu_endianness).dat', '-v', @@ -229,18 +229,18 @@ { # build final .dat -> .obj 'action_name': 'genccode', - 'inputs': [ '../../out/icutmp/icudt<(icu_ver_major)<(icu_endianness).dat' ], - 'outputs': [ '../../out/icudt<(icu_ver_major)<(icu_endianness)_dat.obj' ], + 'inputs': [ '<(SHARED_INTERMEDIATE_DIR)/icutmp/icudt<(icu_ver_major)<(icu_endianness).dat' ], + 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/icudt<(icu_ver_major)<(icu_endianness)_dat.obj' ], 'action': [ '../../<(CONFIGURATION_NAME)/genccode', '-o', - '-d', '../../out/', + '-d', '<(SHARED_INTERMEDIATE_DIR)/', '-n', 'icudata', '-e', 'icusmdt<(icu_ver_major)', '<@(_inputs)' ], }, ], # This file contains the small ICU data. - 'sources': [ '../../out/icudt<(icu_ver_major)<(icu_endianness)_dat.obj' ], + 'sources': [ '<(SHARED_INTERMEDIATE_DIR)/icudt<(icu_ver_major)<(icu_endianness)_dat.obj' ], } ] ], #end of OS==win and icu_small == true }, { # OS != win 'conditions': [ From 52f6f77bc6b988bbc5079e0856a44a1b65dda89b Mon Sep 17 00:00:00 2001 From: Julien Gilli Date: Tue, 16 Dec 2014 19:56:37 +0000 Subject: [PATCH 07/10] build: fix ICU support build on SmartOS --- tools/icu/icu-generic.gyp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/tools/icu/icu-generic.gyp b/tools/icu/icu-generic.gyp index 896acc697d1..de202dd2162 100644 --- a/tools/icu/icu-generic.gyp +++ b/tools/icu/icu-generic.gyp @@ -122,11 +122,6 @@ '../../deps/icu/source/i18n/alphaindex.cpp', # BOCSU # misc - '../../deps/icu/source/i18n/dtitvfmt.cpp', - '../../deps/icu/source/i18n/dtitvinf.cpp', - '../../deps/icu/source/i18n/dtitv_impl.h', - '../../deps/icu/source/i18n/quantityformatter.cpp', - '../../deps/icu/source/i18n/quantityformatter.h', '../../deps/icu/source/i18n/regexcmp.cpp', '../../deps/icu/source/i18n/regexcmp.h', '../../deps/icu/source/i18n/regexcst.h', @@ -139,9 +134,7 @@ '../../deps/icu/source/i18n/region.cpp', '../../deps/icu/source/i18n/region_impl.h', '../../deps/icu/source/i18n/reldatefmt.cpp', - '../../deps/icu/source/i18n/reldatefmt.h', - '../../deps/icu/source/i18n/measfmt.h', - '../../deps/icu/source/i18n/measfmt.cpp', + '../../deps/icu/source/i18n/reldatefmt.h' '../../deps/icu/source/i18n/scientificformathelper.cpp', '../../deps/icu/source/i18n/tmunit.cpp', '../../deps/icu/source/i18n/tmutamt.cpp', @@ -395,13 +388,18 @@ '../../deps/icu/source/common/ushape.cpp', '../../deps/icu/source/common/usprep.cpp', '../../deps/icu/source/common/uts46.cpp', - ]}]], + ]}], + [ 'OS == "solaris"', { 'defines': [ + '_XOPEN_SOURCE_EXTENDED=0', + ]}], + ], 'include_dirs': [ '../../deps/icu/source/common', ], 'defines': [ 'U_COMMON_IMPLEMENTATION=1', ], + 'cflags_c': ['-std=c99'], 'export_dependent_settings': [ 'icu_uconfig', 'icu_uconfig_target' ], 'direct_dependent_settings': { 'include_dirs': [ @@ -429,6 +427,12 @@ '<@(icu_src_io)', '<@(icu_src_stubdata)', ], + 'sources!': [ + '../../deps/icu/source/tools/toolutil/udbgutil.cpp', + '../../deps/icu/source/tools/toolutil/udbgutil.h', + '../../deps/icu/source/tools/toolutil/dbgutil.cpp', + '../../deps/icu/source/tools/toolutil/dbgutil.h', + ], 'include_dirs': [ '../../deps/icu/source/common', '../../deps/icu/source/i18n', @@ -442,6 +446,12 @@ 'U_TOOLUTIL_IMPLEMENTATION=1', #'DEBUG=0', # http://bugs.icu-project.org/trac/ticket/10977 ], + 'cflags_c': ['-std=c99'], + 'conditions': [ + ['OS == "solaris"', { + 'defines': [ '_XOPEN_SOURCE_EXTENDED=0' ] + }] + ], 'direct_dependent_settings': { 'include_dirs': [ '../../deps/icu/source/common', From f758028e9f7ed4f3ebd2bc7d6efa34eb8dc95c8a Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Wed, 17 Dec 2014 17:29:27 -0800 Subject: [PATCH 08/10] build: i18n: cleanup and respond to review Respond to review comments from @misterdjules: * Remove the prepare-icu-source.sh mechanism for creating an embedded deps/icu-small as per 7cb4aad7f3c4da2910146d8ff7750cb27ccb664f - it was not currently used and thus deadwood. * Updated README.md to clarify some of the options: that 'none' is the default for --with-intl, and that --with-icu-source works with 'small-icu' * clarified the test-intl.js test case * cleaned up and added documentation in configure and in nodedownload.py --- README.md | 12 ++++-- configure | 30 +++++++-------- test/simple/test-intl.js | 40 +++++++++++++------- tools/configure.d/nodedownload.py | 12 ++---- tools/icu/prepare-icu-source.sh | 57 ----------------------------- tools/icu/trim-uncompiled-source.sh | 21 ----------- 6 files changed, 51 insertions(+), 121 deletions(-) delete mode 100644 tools/icu/prepare-icu-source.sh delete mode 100644 tools/icu/trim-uncompiled-source.sh diff --git a/README.md b/README.md index bb31c53c018..548cebd69a5 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,7 @@ vcbuild full-icu #### Build with no Intl support `:-(` The `Intl` object will not be available. +This is the default at present, so this option is not normally needed. Unix/Macintosh: @@ -152,18 +153,21 @@ pkg-config --modversion icu-i18n && ./configure --with-intl=system-icu #### Build with a specific ICU: -You can find other ICU releases at [icu4c-**##.#**-src.tgz](http://icu-project.org/download) (or `.zip`) +You can find other ICU releases at +[the ICU homepage](http://icu-project.org/download). +Download the file named something like `icu4c-**##.#**-src.tgz` (or +`.zip`). Unix/Macintosh: from an already-unpacked ICU ```sh -./configure --with-intl=full-icu --with-icu-source=/path/to/icu +./configure --with-intl=[small-icu,full-icu] --with-icu-source=/path/to/icu ``` Unix/Macintosh: from a local ICU tarball ```sh -./configure --with-intl=full-icu --with-icu-source=/path/to/icu.tgz +./configure --with-intl=[small-icu,full-icu] --with-icu-source=/path/to/icu.tgz ``` Unix/Macintosh: from a tarball URL @@ -174,7 +178,7 @@ Unix/Macintosh: from a tarball URL Windows: first unpack latest ICU to `deps/icu` [icu4c-**##.#**-src.tgz](http://icu-project.org/download) (or `.zip`) - as `deps/icu` (You'll have: `deps/icu/source/...`) + as `deps/icu` (You'll have: `deps/icu/source/...`) ```sh vcbuild full-icu diff --git a/configure b/configure index f56b9bc55ae..5cbca755017 100755 --- a/configure +++ b/configure @@ -826,13 +826,21 @@ def configure_intl(o): # Note: non-ICU implementations could use other 'with_intl' # values. + # this is just the 'deps' dir. Used for unpacking. icu_parent_path = os.path.join(root_dir, 'deps') + + # The full path to the ICU source directory. icu_full_path = os.path.join(icu_parent_path, 'icu') - icu_small_path = os.path.join(icu_parent_path, 'icu-small') - icu_small_tag = os.path.join(icu_full_path, 'is-small-icu.txt') + + # icu-tmp is used to download and unpack the ICU tarball. icu_tmp_path = os.path.join(icu_parent_path, 'icu-tmp') - ## Has the user specified an ICU source path? + # --with-icu-source processing + # first, check that they didn't pass --with-icu-source=deps/icu + if with_icu_source and os.path.abspath(icu_full_path) == os.path.abspath(with_icu_source): + print 'Ignoring redundant --with-icu-source=%s' % (with_icu_source) + with_icu_source = None + # if with_icu_source is still set, try to use it. if with_icu_source: if os.path.isdir(icu_full_path): print 'Deleting old ICU source: %s' % (icu_full_path) @@ -843,7 +851,8 @@ def configure_intl(o): print '%s -> %s' % (with_icu_source, icu_full_path) shutil.copytree(with_icu_source, icu_full_path) else: - # could be file or URL. Set up temporary area + # could be file or URL. + # Set up temporary area if os.path.isdir(icu_tmp_path): shutil.rmtree(icu_tmp_path) os.mkdir(icu_tmp_path) @@ -866,19 +875,6 @@ def configure_intl(o): print ' Error: --with-icu-source=%s did not result in an "icu" dir.' % with_icu_source shutil.rmtree(icu_tmp_path) sys.exit(1) - - elif with_intl == 'small-icu': - ## Use (or not) an embedded small-icu. - if not os.path.isdir(icu_full_path) and os.path.isdir(icu_small_path): - # deps/small-icu -> deps/icu - print 'Copying small ICU %s to %s' % (icu_small_path, icu_full_path) - shutil.copytree(icu_small_path, icu_full_path) - #else: - # print 'Not copying %s to %s' % (icu_small_path, icu_full_path) - elif os.path.isfile(icu_small_tag): - ## Not small ICU nor custom path. Delete the old deps/icu - print 'deleting small-icu %s for --with-intl=%s' % (icu_full_path, with_intl) - shutil.rmtree(icu_full_path) # ICU mode. (icu-generic.gyp) byteorder = sys.byteorder diff --git a/test/simple/test-intl.js b/test/simple/test-intl.js index a7618bc7c84..606540bf7fa 100644 --- a/test/simple/test-intl.js +++ b/test/simple/test-intl.js @@ -22,13 +22,18 @@ var common = require('../common'); var assert = require('assert'); +// does node think that i18n was enabled? var enablei18n = process.config.variables.v8_enable_i18n_support; if (enablei18n === undefined) { enablei18n = false; } +// is the Intl object present? var haveIntl = ( global.Intl != undefined ); +// Returns true if no specific locale ids were configured (i.e. "all") +// Else, returns true if loc is in the configured list +// Else, returns false function haveLocale(loc) { var locs = process.config.variables.icu_locales.split(','); if ( locs.indexOf(loc) !== -1 ) { @@ -44,36 +49,45 @@ if (!haveIntl) { } else { assert.equal(enablei18n, true, '"Intl" object is present but v8_enable_i18n_support is ' + enablei18n + '. Is this test out of date?'); - // check with a Formatter + // Construct a new date at the beginning of Unix time var date0 = new Date(0); + + // Use the GMT time zone var GMT = 'Etc/GMT'; - var optsGMT = {timeZone: GMT}; - var expectString0 = '1/1/1970, 12:00:00 AM'; // epoch + // Construct an English formatter. Should format to "Jan 70" var dtf = new Intl.DateTimeFormat(['en'], {timeZone: GMT, month: 'short', year: '2-digit'}); + // Should we run this part of the test? if ( !process.config.variables.icu_locales // if no list specified or.. || haveLocale('en') ) { // list contains 'en' then continue + var localeString; // Check with toLocaleString - var localeString1 = dtf.format(date0); - assert.equal(localeString1, 'Jan 70'); - - var localeString0 = date0.toLocaleString(['en'], optsGMT); - assert.equal(localeString0, expectString0); - + localeString = dtf.format(date0); + assert.equal(localeString, 'Jan 70'); + + // Options to request GMT + var optsGMT = {timeZone: GMT}; + + // Test format + localeString = date0.toLocaleString(['en'], optsGMT); + assert.equal(localeString, '1/1/1970, 12:00:00 AM'); + // number format assert.equal(new Intl.NumberFormat(['en']).format(12345.67890), '12,345.679'); - - var coll = new Intl.Collator(['en'],{sensitivity:'base',ignorePunctuation:true}); - + + var collOpts = { sensitivity: 'base', ignorePunctuation: true }; + var coll = new Intl.Collator(['en'], collOpts); + assert.equal(coll.compare('blackbird', 'black-bird'), 0, 'ignore punctuation failed'); - assert.equal(coll.compare('blackbird', 'red-bird'), -1, 'compare less failed'); assert.equal(coll.compare('bluebird', 'blackbird'), 1, 'compare greater failed'); assert.equal(coll.compare('Bluebird', 'bluebird'), 0, 'ignore case failed'); assert.equal(coll.compare('\ufb03', 'ffi'), 0, 'ffi ligature (contraction) failed'); } else { console.log('Skipping detailed Intl tests because English is not listed as supported.'); + // Smoke test. Does it format anything, or fail? + console.log('Date(0) formatted to: ' + dtf.format(date0)); } } diff --git a/tools/configure.d/nodedownload.py b/tools/configure.d/nodedownload.py index 4d805ab9f8b..d47292f343a 100644 --- a/tools/configure.d/nodedownload.py +++ b/tools/configure.d/nodedownload.py @@ -14,8 +14,7 @@ def formatSize(amt): return fpformat.fix(amt / 1024000., 1) def spin(c): - """print out a spinner based on 'c'""" -# spin = "\\|/-" + """print out an ASCII 'spinner' based on the value of counter 'c'""" spin = ".:|'" return (spin[c % len(spin)]) @@ -53,14 +52,8 @@ def md5sum(targetfile): chunk = f.read(1024) return digest.hexdigest() -def unpackWithMode(opener, packedfile, parent_path, mode): - with opener(packedfile, mode) as icuzip: - print ' Extracting source file: %s' % packedfile - icuzip.extractall(parent_path) - def unpack(packedfile, parent_path): - """Unpack packedfile into parent_path. Assumes .zip. Returns parent_path""" - packedsuffix = packedfile.lower().split('.')[-1] # .zip, .tgz etc + """Unpacks packedfile into parent_path. Assumes .zip. Returns parent_path""" if zipfile.is_zipfile(packedfile): with contextlib.closing(zipfile.ZipFile(packedfile, 'r')) as icuzip: print ' Extracting zipfile: %s' % packedfile @@ -72,4 +65,5 @@ def unpack(packedfile, parent_path): icuzip.extractall(parent_path) return parent_path else: + packedsuffix = packedfile.lower().split('.')[-1] # .zip, .tgz etc raise Exception('Error: Don\'t know how to unpack %s with extension %s' % (packedfile, packedsuffix)) diff --git a/tools/icu/prepare-icu-source.sh b/tools/icu/prepare-icu-source.sh deleted file mode 100644 index 7b38eed49a8..00000000000 --- a/tools/icu/prepare-icu-source.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash -# -# Call this to regenerate /deps/icu-small -# - -if [ -d `dirname "$0"` ]; -then - cd `dirname "$0"` -fi - -# now we are in /tools/icu -cd ../.. -# now we are in , hopefully -if [ ! -d 'tools/icu/' ]; -then - echo "$0: error: cannot find tools/icu in" `pwd` - exit 1 -fi - -echo "Preparing ICU source in" `pwd` -if [ -d 'deps/icu/' ]; -then - echo "Deleting" 'deps/icu' - rm -rf deps/icu || exit 1 -fi - -# clean -echo 'Delete ./out' -rm -rf ./out -# delete old ICU tgz -echo 'Delete deps/icu-small' -rm -rf ./deps/icu-small - -echo "Configuring node.. hopefully with ninja" -./configure --with-intl=small-icu --ninja || ./configure --with-intl=small-icu || exit 1 -echo "Building node.." -make || ( ./configure --with-intl=small-icu && make ) || exit 1 - -echo "Leaving our mark" -echo "# Generated. This is the SMALL ICU dir. Don't commit changes against it." > deps/icu/is-small-icu.txt -echo "Great. trimming stuff.." -rm -rfv deps/icu/[Aap]* -rm -rfv deps/icu/source/allinone deps/icu/source/config deps/icu/source/test -rm -rfv deps/icu/source/layout* deps/icu/source/samples deps/icu/source/tools/ctestfw -rm -rfv deps/icu/source/tools/gen[bdns]* deps/icu/source/tools/icuinfo -rm -rfv deps/icu/source/tools/m* deps/icu/source/tools/tzcode -rm -rfv deps/icu/source/data/{curr,lang,misc,region,sprep,unidata,unit,zone} -find deps/icu \( -name 'Makefile*' -o -name '*\.vcx*' \) -print0 | xargs -0 rm -fv -# now, the harsh part. Prove you are compiled! -sh tools/icu/trim-uncompiled-source.sh - -# move data -cp ./out/Release/gen/icutmp/icudt*l.dat deps/icu/source/data/in/ - -mv -v deps/icu deps/icu-small - -du -sh deps/icu-small diff --git a/tools/icu/trim-uncompiled-source.sh b/tools/icu/trim-uncompiled-source.sh deleted file mode 100644 index be6214b52fa..00000000000 --- a/tools/icu/trim-uncompiled-source.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -# -# Don't call this directly. Called by prepare-icu-source.sh -# - -#./out/Release/obj/deps/icu/source/i18n/icui18n.collationsets.o -#./out/Release/obj/deps/icu/source/i18n/icutools.collationsets.o -for file in `find deps/icu/source -name '*.c' -o -name '*.cpp'`; -do - #echo "# ${file}" - base=`basename ${file} .c` - base=`basename ${base} .cpp` - dir=`dirname ${file}` - #echo ${dir}/${base} - if ls "out/Release/obj/${dir}/"*".${base}.o" 2>/dev/null >/dev/null; - then - true - else - rm -fv ${file} - fi -done From c4a22f08a7cb2e8a4189891e27fe72582e7ee8e4 Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Thu, 18 Dec 2014 12:23:10 -0800 Subject: [PATCH 09/10] build: i18n: Don't auto-download ICU unless --download=all Instead of downloading ICU if missing from `deps/icu`, just print a warning unless either `--download=all` or `--download=icu` is set. Add some generic scaffolding in case other modules end up in the same boat in the future. It's harmless to pass `--download=xyzzy`, so, future proof (at least until some xyzzy dependency is integrated). --- README.md | 15 ++++---- configure | 10 +++++- tools/configure.d/nodedownload.py | 58 +++++++++++++++++++++++++++++++ vcbuild.bat | 6 ++-- 4 files changed, 79 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 548cebd69a5..acaf24b372c 100644 --- a/README.md +++ b/README.md @@ -91,19 +91,19 @@ enabled by default. #### "small" (English only) support This option will build with "small" (English only) support, but -the full `Intl` (ECMA-402) APIs. It will download the ICU library -as needed. +the full `Intl` (ECMA-402) APIs. With `--download=all` it will +download the ICU library as needed. Unix/Macintosh: ```sh -./configure --with-intl=small-icu +./configure --with-intl=small-icu --download=all ``` Windows: ```sh -vcbuild small-icu +vcbuild small-icu download-all ``` The `small-icu` mode builds @@ -114,18 +114,19 @@ with English-only data. You can add full data at runtime. #### Build with full ICU support (all locales supported by ICU): -*Note*, this may download ICU if you don't have an ICU in `deps/icu` +With the `--download=all`, this may download ICU if you don't +have an ICU in `deps/icu`. Unix/Macintosh: ```sh -./configure --with-intl=full-icu +./configure --with-intl=full-icu --download=all ``` Windows: ```sh -vcbuild full-icu +vcbuild full-icu download-all ``` #### Build with no Intl support `:-(` diff --git a/configure b/configure index 5cbca755017..51475f03575 100755 --- a/configure +++ b/configure @@ -242,6 +242,11 @@ parser.add_option('--with-etw', dest='with_etw', help='build with ETW (default is true on Windows)') +parser.add_option('--download', + action='store', + dest='download_list', + help=nodedownload.help()) + parser.add_option('--with-icu-path', action='store', dest='with_icu_path', @@ -310,6 +315,8 @@ parser.add_option('--xcode', (options, args) = parser.parse_args() +# set up auto-download list +auto_downloads = nodedownload.parse(options.download_list) def b(value): """Returns the string 'true' if value is truthy, 'false' otherwise.""" @@ -743,7 +750,8 @@ def configure_intl(o): local = url.split('/')[-1] targetfile = os.path.join(root_dir, 'deps', local) if not os.path.isfile(targetfile): - nodedownload.retrievefile(url, targetfile) + if nodedownload.candownload(auto_downloads, "icu"): + nodedownload.retrievefile(url, targetfile) else: print ' Re-using existing %s' % targetfile if os.path.isfile(targetfile): diff --git a/tools/configure.d/nodedownload.py b/tools/configure.d/nodedownload.py index d47292f343a..e24efd865f3 100644 --- a/tools/configure.d/nodedownload.py +++ b/tools/configure.d/nodedownload.py @@ -67,3 +67,61 @@ def unpack(packedfile, parent_path): else: packedsuffix = packedfile.lower().split('.')[-1] # .zip, .tgz etc raise Exception('Error: Don\'t know how to unpack %s with extension %s' % (packedfile, packedsuffix)) + +# List of possible "--download=" types. +download_types = set(['icu']) + +# Default options for --download. +download_default = "none" + +def help(): + """This function calculates the '--help' text for '--download'.""" + return """Select which packages may be auto-downloaded. +valid values are: none, all, %s. (default is "%s").""" % (", ".join(download_types), download_default) + +def set2dict(keys, value=None): + """Convert some keys (iterable) to a dict.""" + return dict((key, value) for (key) in keys) + +def parse(opt): + """This function parses the options to --download and returns a set such as { icu: true }, etc. """ + if not opt: + opt = download_default + + theOpts = set(opt.split(',')) + + if 'all' in theOpts: + # all on + return set2dict(download_types, True) + elif 'none' in theOpts: + # all off + return set2dict(download_types, False) + + # OK. Now, process each of the opts. + theRet = set2dict(download_types, False) + for anOpt in opt.split(','): + if not anOpt or anOpt == "": + # ignore stray commas, etc. + continue + elif anOpt is 'all': + # all on + theRet = dict((key, True) for (key) in download_types) + else: + # turn this one on + if anOpt in download_types: + theRet[anOpt] = True + else: + # future proof: ignore unknown types + print 'Warning: ignoring unknown --download= type "%s"' % anOpt + # all done + return theRet + +def candownload(auto_downloads, package): + if not (package in auto_downloads.keys()): + raise Exception('Internal error: "%s" is not in the --downloads list. Check nodedownload.py' % package) + if auto_downloads[package]: + return True + else: + print """Warning: Not downloading package "%s". You could pass "--download=all" + (Windows: "download-all") to try auto-downloading it.""" % package + return False diff --git a/vcbuild.bat b/vcbuild.bat index 02f97fb7c7e..39c656f1878 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -36,6 +36,7 @@ set noperfctr= set noperfctr_arg= set noperfctr_msi_arg= set i18n_arg= +set download_arg= :next-arg if "%1"=="" goto args-done @@ -66,6 +67,7 @@ if /i "%1"=="jslint" set jslint=1&goto arg-ok if /i "%1"=="small-icu" set i18n_arg=%1&goto arg-ok if /i "%1"=="full-icu" set i18n_arg=%1&goto arg-ok if /i "%1"=="intl-none" set i18n_arg=%1&goto arg-ok +if /i "%1"=="download-all" set download_arg="--download=all"&goto arg-ok echo Warning: ignoring invalid command line option `%1`. @@ -97,7 +99,7 @@ if defined NIGHTLY set TAG=nightly-%NIGHTLY% @rem Generate the VS project. SETLOCAL if defined VS100COMNTOOLS call "%VS100COMNTOOLS%\VCVarsQueryRegistry.bat" - python configure %i18n_arg% %debug_arg% %nosnapshot_arg% %noetw_arg% %noperfctr_arg% --dest-cpu=%target_arch% --tag=%TAG% + python configure %download_arg% %i18n_arg% %debug_arg% %nosnapshot_arg% %noetw_arg% %noperfctr_arg% --dest-cpu=%target_arch% --tag=%TAG% if errorlevel 1 goto create-msvs-files-failed if not exist node.sln goto create-msvs-files-failed echo Project files generated. @@ -234,7 +236,7 @@ python tools/closure_linter/closure_linter/gjslint.py --unix_mode --strict --noj goto exit :help -echo vcbuild.bat [debug/release] [msi] [test-all/test-uv/test-internet/test-pummel/test-simple/test-message] [clean] [noprojgen] [small-icu/full-icu/intl-none] [nobuild] [nosign] [x86/x64] +echo vcbuild.bat [debug/release] [msi] [test-all/test-uv/test-internet/test-pummel/test-simple/test-message] [clean] [noprojgen] [small-icu/full-icu/intl-none] [nobuild] [nosign] [x86/x64] [download-all] echo Examples: echo vcbuild.bat : builds release build echo vcbuild.bat debug : builds debug build From 98287929349b80390af337a8a76fac98cf82e58e Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Fri, 2 Jan 2015 15:49:16 -0800 Subject: [PATCH 10/10] build: i18n: `make distclean` should remove deps/icu stuff With this change, `make distclean` will delete these items, which aren't in the source repo: * `icu_config.gypi` * `deps/icu` - the ICU source tree * `deps/icu-tmp` - the temporary download/unpack work area * `deps/icu4c*.tgz deps/icu4c*.zip` - ICU source tarballs --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f84c2134499..e463280ac4e 100644 --- a/Makefile +++ b/Makefile @@ -78,10 +78,12 @@ clean: distclean: -rm -rf out - -rm -f config.gypi + -rm -f config.gypi icu_config.gypi -rm -f config.mk -rm -rf node node_g blog.html email.md -rm -rf node_modules + -rm -rf deps/icu + -rm -rf deps/icu4c*.tgz deps/icu4c*.zip deps/icu-tmp test: all $(PYTHON) tools/test.py --mode=release simple message