Skip to content

Commit a308395

Browse files
srl295trevnorris
authored andcommitted
build: i18n: add icu config options
Make "--with-intl=none" the default and add "intl-none" option to vcbuild.bat. If icu data is missing print a warning unless either --download=all or --download=icu is set. If set then automatically download, verify (MD5) and unpack the ICU data if not already available. 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 Add the configuration option "--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 Add the configuration option "--with-icu-locals=...". Allows choosing which locales are used in the "small-icu" case. Example: configure --with-intl=small-icu --with-icu-locales=tlh,grc,nl (Also note that as of this writing, neither Klingon nor Ancient Greek are in upstream CLDR data. Serving suggestion only.) 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 the following can run properly: python tools/test.py simple 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. Also: * Update distclean to remove icu related files * Refactor some code into tools/configure.d/nodedownload.py * Update docs * Add test PR-URL: nodejs/node-v0.x-archive#8719 Fixes: nodejs/node-v0.x-archive#7676 (comment) [[email protected] small change to test's whitespace and logic] Signed-off-by: Trevor Norris <[email protected]>
1 parent 6a03fce commit a308395

File tree

10 files changed

+616
-59
lines changed

10 files changed

+616
-59
lines changed

Diff for: .gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ ipch/
4646
email.md
4747
deps/v8-*
4848
deps/icu
49+
deps/icu*.zip
50+
deps/icu*.tgz
51+
deps/icu-tmp
4952
./node_modules
5053
.svn/
5154

Diff for: Makefile

+3-1
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,12 @@ clean:
7878

7979
distclean:
8080
-rm -rf out
81-
-rm -f config.gypi
81+
-rm -f config.gypi icu_config.gypi
8282
-rm -f config.mk
8383
-rm -rf node node_g blog.html email.md
8484
-rm -rf node_modules
85+
-rm -rf deps/icu
86+
-rm -rf deps/icu4c*.tgz deps/icu4c*.zip deps/icu-tmp
8587

8688
test: all
8789
$(PYTHON) tools/test.py --mode=release simple message

Diff for: README.md

+82-9
Original file line numberDiff line numberDiff line change
@@ -83,31 +83,104 @@ make doc
8383
man doc/node.1
8484
```
8585

86-
### To build `Intl` (ECMA-402) support:
86+
### `Intl` (ECMA-402) support:
8787

88-
*Note:* more docs, including how to reduce disk footprint, are on
88+
[Intl](https://github.com/joyent/node/wiki/Intl) support is not
89+
enabled by default.
90+
91+
#### "small" (English only) support
92+
93+
This option will build with "small" (English only) support, but
94+
the full `Intl` (ECMA-402) APIs. With `--download=all` it will
95+
download the ICU library as needed.
96+
97+
Unix/Macintosh:
98+
99+
```sh
100+
./configure --with-intl=small-icu --download=all
101+
```
102+
103+
Windows:
104+
105+
```sh
106+
vcbuild small-icu download-all
107+
```
108+
109+
The `small-icu` mode builds
110+
with English-only data. You can add full data at runtime.
111+
112+
*Note:* more docs are on
89113
[the wiki](https://github.com/joyent/node/wiki/Intl).
90114

91-
#### Use existing installed ICU (Unix/Macintosh only):
115+
#### Build with full ICU support (all locales supported by ICU):
116+
117+
With the `--download=all`, this may download ICU if you don't
118+
have an ICU in `deps/icu`.
119+
120+
Unix/Macintosh:
92121

93122
```sh
94-
pkg-config --modversion icu-i18n && ./configure --with-intl=system-icu
123+
./configure --with-intl=full-icu --download=all
95124
```
96125

97-
#### Build ICU from source:
126+
Windows:
98127

99-
First: Unpack latest ICU
100-
[icu4c-**##.#**-src.tgz](http://icu-project.org/download) (or `.zip`)
101-
as `deps/icu` (You'll have: `deps/icu/source/...`)
128+
```sh
129+
vcbuild full-icu download-all
130+
```
131+
132+
#### Build with no Intl support `:-(`
133+
134+
The `Intl` object will not be available.
135+
This is the default at present, so this option is not normally needed.
102136

103137
Unix/Macintosh:
104138

105139
```sh
106-
./configure --with-intl=full-icu
140+
./configure --with-intl=none
107141
```
108142

109143
Windows:
110144

145+
```sh
146+
vcbuild intl-none
147+
```
148+
149+
#### Use existing installed ICU (Unix/Macintosh only):
150+
151+
```sh
152+
pkg-config --modversion icu-i18n && ./configure --with-intl=system-icu
153+
```
154+
155+
#### Build with a specific ICU:
156+
157+
You can find other ICU releases at
158+
[the ICU homepage](http://icu-project.org/download).
159+
Download the file named something like `icu4c-**##.#**-src.tgz` (or
160+
`.zip`).
161+
162+
Unix/Macintosh: from an already-unpacked ICU
163+
164+
```sh
165+
./configure --with-intl=[small-icu,full-icu] --with-icu-source=/path/to/icu
166+
```
167+
168+
Unix/Macintosh: from a local ICU tarball
169+
170+
```sh
171+
./configure --with-intl=[small-icu,full-icu] --with-icu-source=/path/to/icu.tgz
172+
```
173+
174+
Unix/Macintosh: from a tarball URL
175+
176+
```sh
177+
./configure --with-intl=full-icu --with-icu-source=http://url/to/icu.tgz
178+
```
179+
180+
Windows: first unpack latest ICU to `deps/icu`
181+
[icu4c-**##.#**-src.tgz](http://icu-project.org/download) (or `.zip`)
182+
as `deps/icu` (You'll have: `deps/icu/source/...`)
183+
111184
```sh
112185
vcbuild full-icu
113186
```

Diff for: configure

+130-7
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,19 @@ import re
66
import shlex
77
import subprocess
88
import sys
9+
import shutil
10+
import string
911

1012
CC = os.environ.get('CC', 'cc')
1113

1214
root_dir = os.path.dirname(__file__)
1315
sys.path.insert(0, os.path.join(root_dir, 'tools', 'gyp', 'pylib'))
1416
from gyp.common import GetFlavor
1517

18+
# imports in tools/configure.d
19+
sys.path.insert(0, os.path.join(root_dir, 'tools', 'configure.d'))
20+
import nodedownload
21+
1622
# parse our options
1723
parser = optparse.OptionParser()
1824

@@ -236,16 +242,31 @@ parser.add_option('--with-etw',
236242
dest='with_etw',
237243
help='build with ETW (default is true on Windows)')
238244

245+
parser.add_option('--download',
246+
action='store',
247+
dest='download_list',
248+
help=nodedownload.help())
249+
239250
parser.add_option('--with-icu-path',
240251
action='store',
241252
dest='with_icu_path',
242253
help='Path to icu.gyp (ICU i18n, Chromium version only.)')
243254

255+
parser.add_option('--with-icu-locales',
256+
action='store',
257+
dest='with_icu_locales',
258+
help='Comma-separated list of locales for "small-icu". Default: "root,en". "root" is assumed.')
259+
244260
parser.add_option('--with-intl',
245261
action='store',
246262
dest='with_intl',
247263
help='Intl mode: none, full-icu, small-icu (default is none)')
248264

265+
parser.add_option('--with-icu-source',
266+
action='store',
267+
dest='with_icu_source',
268+
help='Intl mode: optional local path to icu/ dir, or path/URL of icu source archive.')
269+
249270
parser.add_option('--with-perfctr',
250271
action='store_true',
251272
dest='with_perfctr',
@@ -294,6 +315,8 @@ parser.add_option('--xcode',
294315

295316
(options, args) = parser.parse_args()
296317

318+
# set up auto-download list
319+
auto_downloads = nodedownload.parse(options.download_list)
297320

298321
def b(value):
299322
"""Returns the string 'true' if value is truthy, 'false' otherwise."""
@@ -712,6 +735,35 @@ def glob_to_var(dir_base, dir_sub):
712735
return list
713736

714737
def configure_intl(o):
738+
icus = [
739+
{
740+
'url': 'http://download.icu-project.org/files/icu4c/54.1/icu4c-54_1-src.zip',
741+
# from https://ssl.icu-project.org/files/icu4c/54.1/icu4c-src-54_1.md5:
742+
'md5': '6b89d60e2f0e140898ae4d7f72323bca',
743+
},
744+
]
745+
def icu_download(path):
746+
# download ICU, if needed
747+
for icu in icus:
748+
url = icu['url']
749+
md5 = icu['md5']
750+
local = url.split('/')[-1]
751+
targetfile = os.path.join(root_dir, 'deps', local)
752+
if not os.path.isfile(targetfile):
753+
if nodedownload.candownload(auto_downloads, "icu"):
754+
nodedownload.retrievefile(url, targetfile)
755+
else:
756+
print ' Re-using existing %s' % targetfile
757+
if os.path.isfile(targetfile):
758+
sys.stdout.write(' Checking file integrity with MD5:\r')
759+
gotmd5 = nodedownload.md5sum(targetfile)
760+
print ' MD5: %s %s' % (gotmd5, targetfile)
761+
if (md5 == gotmd5):
762+
return targetfile
763+
else:
764+
print ' Expected: %s *MISMATCH*' % md5
765+
print '\n ** Corrupted ZIP? Delete %s to retry download.\n' % targetfile
766+
return None
715767
icu_config = {
716768
'variables': {}
717769
}
@@ -723,11 +775,11 @@ def configure_intl(o):
723775
write(icu_config_name, do_not_edit +
724776
pprint.pformat(icu_config, indent=2) + '\n')
725777

726-
# small ICU is off by default.
727778
# always set icu_small, node.gyp depends on it being defined.
728779
o['variables']['icu_small'] = b(False)
729780

730781
with_intl = options.with_intl
782+
with_icu_source = options.with_icu_source
731783
have_icu_path = bool(options.with_icu_path)
732784
if have_icu_path and with_intl:
733785
print 'Error: Cannot specify both --with-icu-path and --with-intl'
@@ -739,13 +791,26 @@ def configure_intl(o):
739791
o['variables']['icu_gyp_path'] = options.with_icu_path
740792
return
741793
# --with-intl=<with_intl>
794+
# set the default
795+
if with_intl is None:
796+
with_intl = 'none' # The default mode of Intl
797+
# sanity check localelist
798+
if options.with_icu_locales and (with_intl != 'small-icu'):
799+
print 'Error: --with-icu-locales only makes sense with --with-intl=small-icu'
800+
sys.exit(1)
742801
if with_intl == 'none' or with_intl is None:
743802
o['variables']['v8_enable_i18n_support'] = 0
744803
return # no Intl
745804
elif with_intl == 'small-icu':
746805
# small ICU (English only)
747806
o['variables']['v8_enable_i18n_support'] = 1
748807
o['variables']['icu_small'] = b(True)
808+
with_icu_locales = options.with_icu_locales
809+
if not with_icu_locales:
810+
with_icu_locales = 'root,en'
811+
locs = set(with_icu_locales.split(','))
812+
locs.add('root') # must have root
813+
o['variables']['icu_locales'] = string.join(locs,',')
749814
elif with_intl == 'full-icu':
750815
# full ICU
751816
o['variables']['v8_enable_i18n_support'] = 1
@@ -769,20 +834,78 @@ def configure_intl(o):
769834
# Note: non-ICU implementations could use other 'with_intl'
770835
# values.
771836

837+
# this is just the 'deps' dir. Used for unpacking.
838+
icu_parent_path = os.path.join(root_dir, 'deps')
839+
840+
# The full path to the ICU source directory.
841+
icu_full_path = os.path.join(icu_parent_path, 'icu')
842+
843+
# icu-tmp is used to download and unpack the ICU tarball.
844+
icu_tmp_path = os.path.join(icu_parent_path, 'icu-tmp')
845+
846+
# --with-icu-source processing
847+
# first, check that they didn't pass --with-icu-source=deps/icu
848+
if with_icu_source and os.path.abspath(icu_full_path) == os.path.abspath(with_icu_source):
849+
print 'Ignoring redundant --with-icu-source=%s' % (with_icu_source)
850+
with_icu_source = None
851+
# if with_icu_source is still set, try to use it.
852+
if with_icu_source:
853+
if os.path.isdir(icu_full_path):
854+
print 'Deleting old ICU source: %s' % (icu_full_path)
855+
shutil.rmtree(icu_full_path)
856+
# now, what path was given?
857+
if os.path.isdir(with_icu_source):
858+
# it's a path. Copy it.
859+
print '%s -> %s' % (with_icu_source, icu_full_path)
860+
shutil.copytree(with_icu_source, icu_full_path)
861+
else:
862+
# could be file or URL.
863+
# Set up temporary area
864+
if os.path.isdir(icu_tmp_path):
865+
shutil.rmtree(icu_tmp_path)
866+
os.mkdir(icu_tmp_path)
867+
icu_tarball = None
868+
if os.path.isfile(with_icu_source):
869+
# it's a file. Try to unpack it.
870+
icu_tarball = with_icu_source
871+
else:
872+
# Can we download it?
873+
local = os.path.join(icu_tmp_path, with_icu_source.split('/')[-1]) # local part
874+
icu_tarball = nodedownload.retrievefile(with_icu_source, local)
875+
# continue with "icu_tarball"
876+
nodedownload.unpack(icu_tarball, icu_tmp_path)
877+
# Did it unpack correctly? Should contain 'icu'
878+
tmp_icu = os.path.join(icu_tmp_path, 'icu')
879+
if os.path.isdir(tmp_icu):
880+
os.rename(tmp_icu, icu_full_path)
881+
shutil.rmtree(icu_tmp_path)
882+
else:
883+
print ' Error: --with-icu-source=%s did not result in an "icu" dir.' % with_icu_source
884+
shutil.rmtree(icu_tmp_path)
885+
sys.exit(1)
886+
772887
# ICU mode. (icu-generic.gyp)
773888
byteorder = sys.byteorder
774889
o['variables']['icu_gyp_path'] = 'tools/icu/icu-generic.gyp'
775890
# ICU source dir relative to root
776-
icu_full_path = os.path.join(root_dir, 'deps/icu')
777891
o['variables']['icu_path'] = icu_full_path
778892
if not os.path.isdir(icu_full_path):
779-
print 'Error: ICU path is not a directory: %s' % (icu_full_path)
893+
print '* ECMA-402 (Intl) support didn\'t find ICU in %s..' % (icu_full_path)
894+
# can we download (or find) a zipfile?
895+
localzip = icu_download(icu_full_path)
896+
if localzip:
897+
nodedownload.unpack(localzip, icu_parent_path)
898+
if not os.path.isdir(icu_full_path):
899+
print ' Cannot build Intl without ICU in %s.' % (icu_full_path)
900+
print ' (Fix, or disable with "--with-intl=none" )'
780901
sys.exit(1)
902+
else:
903+
print '* Using ICU in %s' % (icu_full_path)
781904
# Now, what version of ICU is it? We just need the "major", such as 54.
782905
# uvernum.h contains it as a #define.
783906
uvernum_h = os.path.join(icu_full_path, 'source/common/unicode/uvernum.h')
784907
if not os.path.isfile(uvernum_h):
785-
print 'Error: could not load %s - is ICU installed?' % uvernum_h
908+
print ' Error: could not load %s - is ICU installed?' % uvernum_h
786909
sys.exit(1)
787910
icu_ver_major = None
788911
matchVerExp = r'^\s*#define\s+U_ICU_VERSION_SHORT\s+"([^"]*)".*'
@@ -792,7 +915,7 @@ def configure_intl(o):
792915
if m:
793916
icu_ver_major = m.group(1)
794917
if not icu_ver_major:
795-
print 'Could not read U_ICU_VERSION_SHORT version from %s' % uvernum_h
918+
print ' Could not read U_ICU_VERSION_SHORT version from %s' % uvernum_h
796919
sys.exit(1)
797920
icu_endianness = sys.byteorder[0]; # TODO(srl295): EBCDIC should be 'e'
798921
o['variables']['icu_ver_major'] = icu_ver_major
@@ -819,8 +942,8 @@ def configure_intl(o):
819942
# this is the icudt*.dat file which node will be using (platform endianness)
820943
o['variables']['icu_data_file'] = icu_data_file
821944
if not os.path.isfile(icu_data_path):
822-
print 'Error: ICU prebuilt data file %s does not exist.' % icu_data_path
823-
print 'See the README.md.'
945+
print ' Error: ICU prebuilt data file %s does not exist.' % icu_data_path
946+
print ' See the README.md.'
824947
# .. and we're not about to build it from .gyp!
825948
sys.exit(1)
826949
# map from variable name to subdirs

0 commit comments

Comments
 (0)