blob: 7c84b263f69a741a4c7d9a38ffec81a6e40e4211 [file] [log] [blame]
Neil Fuller4568fe42017-06-14 16:11:36 +01001#!/usr/bin/python -B
2
3# Copyright 2017 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Utility methods associated with ICU source and builds."""
18
Luca Stefani23064e22019-01-04 17:09:19 +010019from __future__ import print_function
20
Victor Chang3a44c832021-01-21 16:13:59 +000021import filecmp
Neil Fuller4568fe42017-06-14 16:11:36 +010022import glob
23import os
24import shutil
25import subprocess
26import sys
27
28import i18nutil
Fredrik Roubert36a42992017-08-16 18:35:00 -070029import ziputil
Neil Fuller4568fe42017-06-14 16:11:36 +010030
Fredrik Rouberta1410f02018-04-20 21:23:49 +020031def cldrDir():
32 """Returns the location of CLDR in the Android source tree."""
33 android_build_top = i18nutil.GetAndroidRootOrDie()
34 cldr_dir = os.path.realpath('%s/external/cldr' % android_build_top)
35 i18nutil.CheckDirExists(cldr_dir, 'external/cldr')
36 return cldr_dir
37
38
Neil Fuller4568fe42017-06-14 16:11:36 +010039def icuDir():
40 """Returns the location of ICU in the Android source tree."""
41 android_build_top = i18nutil.GetAndroidRootOrDie()
42 icu_dir = os.path.realpath('%s/external/icu' % android_build_top)
43 i18nutil.CheckDirExists(icu_dir, 'external/icu')
44 return icu_dir
45
46
47def icu4cDir():
48 """Returns the location of ICU4C in the Android source tree."""
49 icu4c_dir = os.path.realpath('%s/icu4c/source' % icuDir())
50 i18nutil.CheckDirExists(icu4c_dir, 'external/icu/icu4c/source')
51 return icu4c_dir
52
53
54def icu4jDir():
55 """Returns the location of ICU4J in the Android source tree."""
56 icu4j_dir = os.path.realpath('%s/icu4j' % icuDir())
57 i18nutil.CheckDirExists(icu4j_dir, 'external/icu/icu4j')
58 return icu4j_dir
59
60
61def datFile(icu_build_dir):
62 """Returns the location of the ICU .dat file in the specified ICU build dir."""
63 dat_file_pattern = '%s/data/out/tmp/icudt??l.dat' % icu_build_dir
64 dat_files = glob.glob(dat_file_pattern)
65 if len(dat_files) != 1:
Luca Stefani23064e22019-01-04 17:09:19 +010066 print('ERROR: Unexpectedly found %d .dat files (%s). Halting.' % (len(datfiles), datfiles))
Neil Fuller4568fe42017-06-14 16:11:36 +010067 sys.exit(1)
68 dat_file = dat_files[0]
69 return dat_file
70
71
72def PrepareIcuBuild(icu_build_dir):
73 """Sets up an ICU build in the specified (non-existent) directory.
74
75 Creates the directory and runs "runConfigureICU Linux"
76 """
77 # Keep track of the original cwd so we can go back to it at the end.
78 original_working_dir = os.getcwd()
79
80 # Create a directory to run 'make' from.
81 os.mkdir(icu_build_dir)
82 os.chdir(icu_build_dir)
83
84 # Build the ICU tools.
Luca Stefani23064e22019-01-04 17:09:19 +010085 print('Configuring ICU tools...')
Victor Change793e452019-03-12 12:55:44 +000086 subprocess.check_call(['env', 'ICU_DATA_BUILDTOOL_OPTS=--include_uni_core_data', '%s/runConfigureICU' % icu4cDir(), 'Linux'])
Neil Fuller4568fe42017-06-14 16:11:36 +010087
88 os.chdir(original_working_dir)
89
90
91def MakeTzDataFiles(icu_build_dir, iana_tar_file):
92 """Builds and runs the ICU tools in ${icu_Build_dir}/tools/tzcode.
93
94 The tools are run against the specified IANA tzdata .tar.gz.
95 The resulting zoneinfo64.txt is copied into the src directories.
96 """
97 tzcode_working_dir = '%s/tools/tzcode' % icu_build_dir
98
99 # Fix missing files.
100 # The tz2icu tool only picks up icuregions and icuzones if they are in the CWD
101 for icu_data_file in [ 'icuregions', 'icuzones']:
102 icu_data_file_source = '%s/tools/tzcode/%s' % (icu4cDir(), icu_data_file)
103 icu_data_file_symlink = '%s/%s' % (tzcode_working_dir, icu_data_file)
104 os.symlink(icu_data_file_source, icu_data_file_symlink)
105
106 iana_tar_filename = os.path.basename(iana_tar_file)
107 working_iana_tar_file = '%s/%s' % (tzcode_working_dir, iana_tar_filename)
108 shutil.copyfile(iana_tar_file, working_iana_tar_file)
109
Luca Stefani23064e22019-01-04 17:09:19 +0100110 print('Making ICU tz data files...')
Neil Fuller4568fe42017-06-14 16:11:36 +0100111 # The Makefile assumes the existence of the bin directory.
112 os.mkdir('%s/bin' % icu_build_dir)
113
Neil Fullerf881fae2018-06-06 11:57:35 +0100114 # -j1 is needed because the build is not parallelizable. http://b/109641429
115 subprocess.check_call(['make', '-j1', '-C', tzcode_working_dir])
Neil Fuller4568fe42017-06-14 16:11:36 +0100116
117 # Copy the source file to its ultimate destination.
118 zoneinfo_file = '%s/zoneinfo64.txt' % tzcode_working_dir
119 icu_txt_data_dir = '%s/data/misc' % icu4cDir()
Luca Stefani23064e22019-01-04 17:09:19 +0100120 print('Copying zoneinfo64.txt to %s ...' % icu_txt_data_dir)
Neil Fuller4568fe42017-06-14 16:11:36 +0100121 shutil.copy(zoneinfo_file, icu_txt_data_dir)
122
123
124def MakeAndCopyIcuDataFiles(icu_build_dir):
125 """Builds the ICU .dat and .jar files using the current src data.
126
127 The files are copied back into the expected locations in the src tree.
128 """
129 # Keep track of the original cwd so we can go back to it at the end.
130 original_working_dir = os.getcwd()
131
132 # Regenerate the .dat file.
133 os.chdir(icu_build_dir)
Victor Change793e452019-03-12 12:55:44 +0000134 subprocess.check_call(['make', '-j32'])
Neil Fuller4568fe42017-06-14 16:11:36 +0100135
136 # Copy the .dat file to its ultimate destination.
137 icu_dat_data_dir = '%s/stubdata' % icu4cDir()
138 dat_file = datFile(icu_build_dir)
139
Luca Stefani23064e22019-01-04 17:09:19 +0100140 print('Copying %s to %s ...' % (dat_file, icu_dat_data_dir))
Neil Fuller4568fe42017-06-14 16:11:36 +0100141 shutil.copy(dat_file, icu_dat_data_dir)
142
143 # Generate the ICU4J .jar files
Neil Fullerf881fae2018-06-06 11:57:35 +0100144 subprocess.check_call(['make', '-j32', 'icu4j-data'])
Neil Fuller4568fe42017-06-14 16:11:36 +0100145
Victor Chang9977c002021-01-15 18:14:50 +0000146 # Generate the test data in icu4c/source/test/testdata/out
147 subprocess.check_call(['make', '-j32', 'tests'])
148
Neil Fuller4568fe42017-06-14 16:11:36 +0100149 # Copy the ICU4J .jar files to their ultimate destination.
150 icu_jar_data_dir = '%s/main/shared/data' % icu4jDir()
Victor Changc5f2fa02020-05-11 15:44:18 +0100151 jarfiles = glob.glob('data/out/icu4j/*.jar')
152 if len(jarfiles) != 3:
Luca Stefani23064e22019-01-04 17:09:19 +0100153 print('ERROR: Unexpectedly found %d .jar files (%s). Halting.' % (len(jarfiles), jarfiles))
Neil Fuller4568fe42017-06-14 16:11:36 +0100154 sys.exit(1)
155 for jarfile in jarfiles:
Fredrik Roubert36a42992017-08-16 18:35:00 -0700156 icu_jarfile = os.path.join(icu_jar_data_dir, os.path.basename(jarfile))
157 if ziputil.ZipCompare(jarfile, icu_jarfile):
Luca Stefani23064e22019-01-04 17:09:19 +0100158 print('Ignoring %s which is identical to %s ...' % (jarfile, icu_jarfile))
Fredrik Roubert36a42992017-08-16 18:35:00 -0700159 else:
Luca Stefani23064e22019-01-04 17:09:19 +0100160 print('Copying %s to %s ...' % (jarfile, icu_jar_data_dir))
Fredrik Roubert36a42992017-08-16 18:35:00 -0700161 shutil.copy(jarfile, icu_jar_data_dir)
Neil Fuller4568fe42017-06-14 16:11:36 +0100162
Victor Chang9977c002021-01-15 18:14:50 +0000163 testdata_out_dir = '%s/test/testdata/out' % icu4cDir()
164 print('Copying test data to %s ' % testdata_out_dir)
165 if os.path.exists(testdata_out_dir):
166 shutil.rmtree(testdata_out_dir)
167 shutil.copytree('test/testdata/out', testdata_out_dir)
168
Neil Fuller4568fe42017-06-14 16:11:36 +0100169 # Switch back to the original working cwd.
170 os.chdir(original_working_dir)
171
172
173def MakeAndCopyOverlayTzIcuData(icu_build_dir, dest_file):
174 """Makes a .dat file containing just time-zone data.
175
176 The overlay file can be used as an overlay of a full ICU .dat file
177 to provide newer time zone data. Some strings like translated
178 time zone names will be missing, but rules will be correct."""
179 # Keep track of the original cwd so we can go back to it at the end.
180 original_working_dir = os.getcwd()
181
182 # Regenerate the .res files.
183 os.chdir(icu_build_dir)
Victor Change793e452019-03-12 12:55:44 +0000184 subprocess.check_call(['make', '-j32'])
Neil Fuller4568fe42017-06-14 16:11:36 +0100185
186 # The list of ICU resources needed for time zone data overlays.
187 tz_res_names = [
188 'metaZones.res',
189 'timezoneTypes.res',
190 'windowsZones.res',
191 'zoneinfo64.res',
192 ]
193
194 dat_file = datFile(icu_build_dir)
195 icu_package_dat = os.path.basename(dat_file)
196 if not icu_package_dat.endswith('.dat'):
Luca Stefani23064e22019-01-04 17:09:19 +0100197 print('%s does not end with .dat' % icu_package_dat)
Neil Fuller4568fe42017-06-14 16:11:36 +0100198 sys.exit(1)
199 icu_package = icu_package_dat[:-4]
200
201 # Create a staging directory to hold the files to go into the overlay .dat
202 res_staging_dir = '%s/overlay_res' % icu_build_dir
203 os.mkdir(res_staging_dir)
204
205 # Copy all the .res files we need from, e.g. ./data/out/build/icudt55l, to the staging directory
206 res_src_dir = '%s/data/out/build/%s' % (icu_build_dir, icu_package)
207 for tz_res_name in tz_res_names:
208 shutil.copy('%s/%s' % (res_src_dir, tz_res_name), res_staging_dir)
209
210 # Create a .lst file to pass to pkgdata.
211 tz_files_file = '%s/tzdata.lst' % res_staging_dir
212 with open(tz_files_file, "a") as tz_files:
213 for tz_res_name in tz_res_names:
214 tz_files.write('%s\n' % tz_res_name)
215
216 icu_lib_dir = '%s/lib' % icu_build_dir
217 pkg_data_bin = '%s/bin/pkgdata' % icu_build_dir
218
219 # Run pkgdata to create a .dat file.
220 icu_env = os.environ.copy()
221 icu_env["LD_LIBRARY_PATH"] = icu_lib_dir
222
223 # pkgdata treats the .lst file it is given as relative to CWD, and the path also affects the
224 # resource names in the .dat file produced so we change the CWD.
225 os.chdir(res_staging_dir)
226
227 # -F : force rebuilding all data
228 # -m common : create a .dat
229 # -v : verbose
230 # -T . : use "." as a temp dir
231 # -d . : use "." as the dest dir
232 # -p <name> : Set the "data name"
233 p = subprocess.Popen(
234 [pkg_data_bin, '-F', '-m', 'common', '-v', '-T', '.', '-d', '.', '-p',
235 icu_package, tz_files_file],
236 env=icu_env)
237 p.wait()
238 if p.returncode != 0:
Luca Stefani23064e22019-01-04 17:09:19 +0100239 print('pkgdata failed with status code: %s' % p.returncode)
Neil Fuller4568fe42017-06-14 16:11:36 +0100240
241 # Copy the .dat to the chosen place / name.
242 generated_dat_file = '%s/%s' % (res_staging_dir, icu_package_dat)
243 shutil.copyfile(generated_dat_file, dest_file)
Luca Stefani23064e22019-01-04 17:09:19 +0100244 print('ICU overlay .dat can be found here: %s' % dest_file)
Neil Fuller4568fe42017-06-14 16:11:36 +0100245
246 # Switch back to the original working cwd.
247 os.chdir(original_working_dir)
248
Victor Chang3a44c832021-01-21 16:13:59 +0000249def RequiredToMakeLangInfo():
250 """ Returns true if icu4c/source/data/misc/langInfo.txt has been re-generated.
251 Returns false if re-generation is not needed.
252 """
253
254 # Generate icu4c/source/data/misc/langInfo.txt by a ICU4J tool
255 langInfo_dst_path = os.path.join(icu4cDir(), 'data/misc/langInfo.txt')
256 print('Building %s' % langInfo_dst_path)
257 langInfo_out_path = '/tmp/langInfo.txt' # path hardcoded in the LocaleDistanceBuilder tool
258 if os.path.exists(langInfo_out_path):
259 os.remove(langInfo_out_path)
260
261 icu4j_dir = icu4jDir()
262 os.chdir(icu4j_dir)
263 subprocess.check_call(['ant', 'icu4jJar'])
264 os.chdir(os.path.join(icu4j_dir, 'tools', 'misc'))
265 subprocess.check_call(['ant', 'jar'])
266 subprocess.check_call([
267 'java',
268 '-cp',
269 'out/lib/icu4j-tools.jar:../../icu4j.jar',
270 'com.ibm.icu.dev.tool.locale.LocaleDistanceBuilder',
271 ])
272 if (filecmp.cmp(langInfo_dst_path, langInfo_out_path)):
273 print('The files {src} and {dst} are the same'.format(src=langInfo_out_path, dst=langInfo_dst_path))
274 return False
275
276 print('Copying {src} to {dst}'.format(src=langInfo_out_path, dst=langInfo_dst_path))
277 shutil.copyfile(langInfo_out_path, langInfo_dst_path)
278 return True
Neil Fullere5daa822018-11-01 20:38:49 +0000279
280def CopyLicenseFiles(target_dir):
281 """Copies ICU license files to the target_dir"""
282
283 license_file = '%s/main/shared/licenses/LICENSE' % icu4jDir()
Luca Stefani23064e22019-01-04 17:09:19 +0100284 print('Copying %s to %s ...' % (license_file, target_dir))
Neil Fullere5daa822018-11-01 20:38:49 +0000285 shutil.copy(license_file, target_dir)
286