blob: 9cf565bbf86c9f3b69c25883f6414f7d9ff6b34d [file] [log] [blame]
Neil Fuller86e72c52017-06-12 15:36:06 +01001#!/usr/bin/python -B
2
Neil Fuller56166d32017-06-12 12:57:10 +01003# 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"""Generates the timezone data files used by Android."""
Neil Fuller86e72c52017-06-12 15:36:06 +010018
Luca Stefani136579e2019-01-04 17:09:55 +010019from __future__ import print_function
20
Neil Fuller86e72c52017-06-12 15:36:06 +010021import glob
Neil Fuller86e72c52017-06-12 15:36:06 +010022import os
Neil Fullercdf56952018-02-20 17:12:37 +000023import re
Neil Fuller86e72c52017-06-12 15:36:06 +010024import subprocess
25import sys
26import tarfile
27import tempfile
28
Neil Fullerad7e81c2017-06-15 16:45:56 +010029sys.path.append('%s/external/icu/tools' % os.environ.get('ANDROID_BUILD_TOP'))
Neil Fuller86e72c52017-06-12 15:36:06 +010030import i18nutil
Neil Fullerfa89b032017-06-14 15:58:26 +010031import icuutil
Neil Fuller56166d32017-06-12 12:57:10 +010032import tzdatautil
Neil Fuller86e72c52017-06-12 15:36:06 +010033
Neil Fuller86e72c52017-06-12 15:36:06 +010034
35# Calculate the paths that are referred to by multiple functions.
36android_build_top = i18nutil.GetAndroidRootOrDie()
Neil Fuller56166d32017-06-12 12:57:10 +010037timezone_dir = os.path.realpath('%s/system/timezone' % android_build_top)
38i18nutil.CheckDirExists(timezone_dir, 'system/timezone')
39
Neil Fuller8cec5612017-10-11 10:38:17 +010040android_host_out = i18nutil.GetAndroidHostOutOrDie()
41
Neil Fuller0480f462019-04-09 19:23:35 +010042zone_compactor_dir = os.path.realpath('%s/system/timezone/input_tools/android' % android_build_top)
43i18nutil.CheckDirExists(zone_compactor_dir, 'system/timezone/input_tools/android')
Neil Fuller56166d32017-06-12 12:57:10 +010044
Neil Fullercdf56952018-02-20 17:12:37 +000045timezone_input_tools_dir = os.path.realpath('%s/input_tools' % timezone_dir)
Neil Fuller56166d32017-06-12 12:57:10 +010046timezone_input_data_dir = os.path.realpath('%s/input_data' % timezone_dir)
47
Neil Fuller35467e12017-06-12 16:55:44 +010048timezone_output_data_dir = '%s/output_data' % timezone_dir
49i18nutil.CheckDirExists(timezone_output_data_dir, 'output_data')
Neil Fuller86e72c52017-06-12 15:36:06 +010050
51tmp_dir = tempfile.mkdtemp('-tzdata')
52
Neil Fullera591fb02018-05-08 17:29:11 +010053def GenerateZicInputFile(extracted_iana_data_dir):
54 # Android APIs assume DST means "summer time" so we follow the rearguard format
55 # introduced in 2018e.
56 zic_input_file_name = 'rearguard.zi'
Neil Fuller86e72c52017-06-12 15:36:06 +010057
Neil Fullera591fb02018-05-08 17:29:11 +010058 # 'NDATA=' is used to remove unnecessary rules files.
59 subprocess.check_call(['make', '-C', extracted_iana_data_dir, 'NDATA=', zic_input_file_name])
60
61 zic_input_file = '%s/%s' % (extracted_iana_data_dir, zic_input_file_name)
62 if not os.path.exists(zic_input_file):
Luca Stefani136579e2019-01-04 17:09:55 +010063 print('Could not find %s' % zic_input_file)
Neil Fullera591fb02018-05-08 17:29:11 +010064 sys.exit(1)
65 return zic_input_file
66
67
68def WriteSetupFile(zic_input_file):
Neil Fuller86e72c52017-06-12 15:36:06 +010069 """Writes the list of zones that ZoneCompactor should process."""
70 links = []
71 zones = []
Neil Fullera591fb02018-05-08 17:29:11 +010072 for line in open(zic_input_file):
73 fields = line.split()
74 if fields:
Neil Fuller4da0af12020-05-19 14:56:15 +010075 line_type = fields[0]
76 if line_type == 'Link':
77 # Each "Link" line requires the creation of a link from an old tz ID to
78 # a new tz ID, and implies the existence of a zone with the old tz ID.
79 #
80 # IANA terminology:
81 # TARGET = the new tz ID, LINK-NAME = the old tz ID
82 target = fields[1]
83 link_name = fields[2]
84 links.append('Link %s %s' % (target, link_name))
85 zones.append('Zone %s' % link_name)
86 elif line_type == 'Zone':
87 # Each "Zone" line indicates the existence of a tz ID.
88 #
89 # IANA terminology:
90 # NAME is the tz ID, other fields like STDOFF, RULES, FORMAT,[UNTIL] are
91 # ignored.
92 name = fields[1]
93 zones.append('Zone %s' % name)
Neil Fuller86e72c52017-06-12 15:36:06 +010094
95 zone_compactor_setup_file = '%s/setup' % tmp_dir
96 setup = open(zone_compactor_setup_file, 'w')
Neil Fuller4da0af12020-05-19 14:56:15 +010097
98 # Ordering requirement from ZoneCompactor: Links must come first.
Neil Fuller86e72c52017-06-12 15:36:06 +010099 for link in sorted(set(links)):
100 setup.write('%s\n' % link)
101 for zone in sorted(set(zones)):
102 setup.write('%s\n' % zone)
103 setup.close()
104 return zone_compactor_setup_file
105
106
Neil Fullercdf56952018-02-20 17:12:37 +0000107def BuildIcuData(iana_data_tar_file):
Neil Fuller86e72c52017-06-12 15:36:06 +0100108 icu_build_dir = '%s/icu' % tmp_dir
109
Neil Fullerfa89b032017-06-14 15:58:26 +0100110 icuutil.PrepareIcuBuild(icu_build_dir)
Neil Fullercdf56952018-02-20 17:12:37 +0000111 icuutil.MakeTzDataFiles(icu_build_dir, iana_data_tar_file)
Neil Fullerfa89b032017-06-14 15:58:26 +0100112
113 # Create ICU system image files.
114 icuutil.MakeAndCopyIcuDataFiles(icu_build_dir)
115
Neil Fuller64bd4112018-11-01 18:14:07 +0000116 icu_overlay_dir = '%s/icu_overlay' % timezone_output_data_dir
117
Neil Fullerfa89b032017-06-14 15:58:26 +0100118 # Create the ICU overlay time zone file.
Neil Fuller64bd4112018-11-01 18:14:07 +0000119 icu_overlay_dat_file = '%s/icu_tzdata.dat' % icu_overlay_dir
Neil Fullerfa89b032017-06-14 15:58:26 +0100120 icuutil.MakeAndCopyOverlayTzIcuData(icu_build_dir, icu_overlay_dat_file)
Neil Fuller86e72c52017-06-12 15:36:06 +0100121
Neil Fuller64bd4112018-11-01 18:14:07 +0000122 # Copy ICU license file(s)
123 icuutil.CopyLicenseFiles(icu_overlay_dir)
124
Neil Fuller86e72c52017-06-12 15:36:06 +0100125
Neil Fullercdf56952018-02-20 17:12:37 +0000126def GetIanaVersion(iana_tar_file):
127 iana_tar_filename = os.path.basename(iana_tar_file)
128 iana_version = re.search('tz(?:data|code)(.+)\\.tar\\.gz', iana_tar_filename).group(1)
Neil Fullerad7e81c2017-06-15 16:45:56 +0100129 return iana_version
130
Neil Fuller8cec5612017-10-11 10:38:17 +0100131
Neil Fullercdf56952018-02-20 17:12:37 +0000132def ExtractTarFile(tar_file, dir):
Luca Stefani136579e2019-01-04 17:09:55 +0100133 print('Extracting %s...' % tar_file)
Neil Fullercdf56952018-02-20 17:12:37 +0000134 if not os.path.exists(dir):
135 os.mkdir(dir)
136 tar = tarfile.open(tar_file, 'r')
137 tar.extractall(dir)
Neil Fuller86e72c52017-06-12 15:36:06 +0100138
Neil Fullercdf56952018-02-20 17:12:37 +0000139
140def BuildZic(iana_tools_dir):
Neil Fuller2aba99c2019-02-26 13:15:34 +0000141 iana_zic_code_tar_file = tzdatautil.GetIanaTarFile(iana_tools_dir, 'tzcode')
Neil Fullercdf56952018-02-20 17:12:37 +0000142 iana_zic_code_version = GetIanaVersion(iana_zic_code_tar_file)
Neil Fuller2aba99c2019-02-26 13:15:34 +0000143 iana_zic_data_tar_file = tzdatautil.GetIanaTarFile(iana_tools_dir, 'tzdata')
Neil Fullercdf56952018-02-20 17:12:37 +0000144 iana_zic_data_version = GetIanaVersion(iana_zic_data_tar_file)
145
Luca Stefani136579e2019-01-04 17:09:55 +0100146 print('Found IANA zic release %s/%s in %s/%s ...' \
Neil Fullered0c59b2020-05-03 21:18:13 +0100147 % (iana_zic_code_version, iana_zic_data_version, iana_zic_code_tar_file,
148 iana_zic_data_tar_file))
Neil Fullercdf56952018-02-20 17:12:37 +0000149
150 zic_build_dir = '%s/zic' % tmp_dir
151 ExtractTarFile(iana_zic_code_tar_file, zic_build_dir)
152 ExtractTarFile(iana_zic_data_tar_file, zic_build_dir)
153
154 # zic
Luca Stefani136579e2019-01-04 17:09:55 +0100155 print('Building zic...')
Neil Fullercdf56952018-02-20 17:12:37 +0000156 # VERSION_DEPS= is to stop the build process looking for files that might not
157 # be present across different versions.
158 subprocess.check_call(['make', '-C', zic_build_dir, 'zic'])
159
160 zic_binary_file = '%s/zic' % zic_build_dir
161 if not os.path.exists(zic_binary_file):
Luca Stefani136579e2019-01-04 17:09:55 +0100162 print('Could not find %s' % zic_binary_file)
Neil Fullera591fb02018-05-08 17:29:11 +0100163 sys.exit(1)
Neil Fullercdf56952018-02-20 17:12:37 +0000164 return zic_binary_file
165
166
167def BuildTzdata(zic_binary_file, extracted_iana_data_dir, iana_data_version):
Luca Stefani136579e2019-01-04 17:09:55 +0100168 print('Generating zic input file...')
Neil Fullera591fb02018-05-08 17:29:11 +0100169 zic_input_file = GenerateZicInputFile(extracted_iana_data_dir)
170
Luca Stefani136579e2019-01-04 17:09:55 +0100171 print('Calling zic...')
Neil Fuller86e72c52017-06-12 15:36:06 +0100172 zic_output_dir = '%s/data' % tmp_dir
173 os.mkdir(zic_output_dir)
Neil Fullera591fb02018-05-08 17:29:11 +0100174 zic_cmd = [zic_binary_file, '-d', zic_output_dir, zic_input_file]
Neil Fuller86e72c52017-06-12 15:36:06 +0100175 subprocess.check_call(zic_cmd)
176
Neil Fullercdf56952018-02-20 17:12:37 +0000177 # ZoneCompactor
Neil Fullera591fb02018-05-08 17:29:11 +0100178 zone_compactor_setup_file = WriteSetupFile(zic_input_file)
Neil Fuller86e72c52017-06-12 15:36:06 +0100179
Luca Stefani136579e2019-01-04 17:09:55 +0100180 print('Calling ZoneCompactor to update tzdata to %s...' % iana_data_version)
Neil Fullerd322be62019-09-16 16:25:03 +0100181
182 tzdatautil.InvokeSoong(android_build_top, ['zone_compactor'])
Neil Fuller86e72c52017-06-12 15:36:06 +0100183
Neil Fullercdf56952018-02-20 17:12:37 +0000184 # Create args for ZoneCompactor
Neil Fullercdf56952018-02-20 17:12:37 +0000185 header_string = 'tzdata%s' % iana_data_version
Neil Fuller35467e12017-06-12 16:55:44 +0100186
Luca Stefani136579e2019-01-04 17:09:55 +0100187 print('Executing ZoneCompactor...')
Neil Fullerd322be62019-09-16 16:25:03 +0100188 command = '%s/bin/zone_compactor' % android_host_out
Neil Fuller35467e12017-06-12 16:55:44 +0100189 iana_output_data_dir = '%s/iana' % timezone_output_data_dir
Neil Fuller61b7d562020-05-21 14:43:47 +0100190 subprocess.check_call([command, zone_compactor_setup_file, zic_output_dir, iana_output_data_dir,
191 header_string])
Neil Fuller56166d32017-06-12 12:57:10 +0100192
193
Neil Fuller78031492020-08-07 21:17:58 +0100194def BuildTzlookupAndTzIds(iana_data_dir):
Neil Fuller8cec5612017-10-11 10:38:17 +0100195 countryzones_source_file = '%s/android/countryzones.txt' % timezone_input_data_dir
Neil Fuller35467e12017-06-12 16:55:44 +0100196 tzlookup_dest_file = '%s/android/tzlookup.xml' % timezone_output_data_dir
Neil Fuller78031492020-08-07 21:17:58 +0100197 tzids_dest_file = '%s/android/tzids.prototxt' % timezone_output_data_dir
Neil Fuller8cec5612017-10-11 10:38:17 +0100198
Neil Fuller78031492020-08-07 21:17:58 +0100199 print('Calling TzLookupGenerator to create tzlookup.xml / tzids.prototxt...')
Neil Fullerd322be62019-09-16 16:25:03 +0100200 tzdatautil.InvokeSoong(android_build_top, ['tzlookup_generator'])
Neil Fuller8cec5612017-10-11 10:38:17 +0100201
Neil Fuller8cec5612017-10-11 10:38:17 +0100202 zone_tab_file = '%s/zone.tab' % iana_data_dir
Neil Fuller008dfcd2020-05-06 16:43:30 +0100203 backward_file = '%s/backward' % iana_data_dir
Neil Fullerd322be62019-09-16 16:25:03 +0100204 command = '%s/bin/tzlookup_generator' % android_host_out
Neil Fuller008dfcd2020-05-06 16:43:30 +0100205 subprocess.check_call([command, countryzones_source_file, zone_tab_file, backward_file,
Neil Fuller78031492020-08-07 21:17:58 +0100206 tzlookup_dest_file, tzids_dest_file])
Neil Fuller56166d32017-06-12 12:57:10 +0100207
Neil Fuller86e72c52017-06-12 15:36:06 +0100208
Neil Fullerf85e8142019-10-28 17:21:55 +0000209def BuildTelephonylookup():
210 telephonylookup_source_file = '%s/android/telephonylookup.txt' % timezone_input_data_dir
211 telephonylookup_dest_file = '%s/android/telephonylookup.xml' % timezone_output_data_dir
212
213 print('Calling TelephonyLookupGenerator to create telephonylookup.xml...')
214 tzdatautil.InvokeSoong(android_build_top, ['telephonylookup_generator'])
215
216 command = '%s/bin/telephonylookup_generator' % android_host_out
217 subprocess.check_call([command, telephonylookup_source_file, telephonylookup_dest_file])
218
219
Neil Fullere2d551b2018-10-29 15:32:06 +0000220def CreateDistroFiles(iana_data_version, output_distro_dir, output_version_file):
Neil Fullerad7e81c2017-06-15 16:45:56 +0100221 create_distro_script = '%s/distro/tools/create-distro.py' % timezone_dir
222
223 tzdata_file = '%s/iana/tzdata' % timezone_output_data_dir
224 icu_file = '%s/icu_overlay/icu_tzdata.dat' % timezone_output_data_dir
225 tzlookup_file = '%s/android/tzlookup.xml' % timezone_output_data_dir
Neil Fullerf85e8142019-10-28 17:21:55 +0000226 telephonylookup_file = '%s/android/telephonylookup.xml' % timezone_output_data_dir
Neil Fullerad7e81c2017-06-15 16:45:56 +0100227
Neil Fullere2d551b2018-10-29 15:32:06 +0000228 distro_file_pattern = '%s/*.zip' % output_distro_dir
229 existing_files = glob.glob(distro_file_pattern)
Neil Fullerad7e81c2017-06-15 16:45:56 +0100230
Luca Stefani136579e2019-01-04 17:09:55 +0100231 print('Removing %s' % existing_files)
Neil Fullerad7e81c2017-06-15 16:45:56 +0100232 for existing_file in existing_files:
233 os.remove(existing_file)
234
235 subprocess.check_call([create_distro_script,
Neil Fullercdf56952018-02-20 17:12:37 +0000236 '-iana_version', iana_data_version,
Neil Fullerad7e81c2017-06-15 16:45:56 +0100237 '-tzdata', tzdata_file,
238 '-icu', icu_file,
239 '-tzlookup', tzlookup_file,
Neil Fullerf85e8142019-10-28 17:21:55 +0000240 '-telephonylookup', telephonylookup_file,
Neil Fullere2d551b2018-10-29 15:32:06 +0000241 '-output_distro_dir', output_distro_dir,
242 '-output_version_file', output_version_file])
Neil Fullerad7e81c2017-06-15 16:45:56 +0100243
Neil Fullerad3c8762017-10-20 15:58:57 +0100244def UpdateTestFiles():
245 testing_data_dir = '%s/testing/data' % timezone_dir
246 update_test_files_script = '%s/create-test-data.sh' % testing_data_dir
247 subprocess.check_call([update_test_files_script], cwd=testing_data_dir)
248
Neil Fuller86e72c52017-06-12 15:36:06 +0100249
250# Run with no arguments from any directory, with no special setup required.
251# See http://www.iana.org/time-zones/ for more about the source of this data.
252def main():
Luca Stefani136579e2019-01-04 17:09:55 +0100253 print('Source data file structure: %s' % timezone_input_data_dir)
254 print('Source tools file structure: %s' % timezone_input_tools_dir)
Neil Fullered0c59b2020-05-03 21:18:13 +0100255 print('Intermediate / working dir: %s' % tmp_dir)
Luca Stefani136579e2019-01-04 17:09:55 +0100256 print('Output data file structure: %s' % timezone_output_data_dir)
Neil Fuller56166d32017-06-12 12:57:10 +0100257
Neil Fuller8cec5612017-10-11 10:38:17 +0100258 iana_input_data_dir = '%s/iana' % timezone_input_data_dir
Neil Fuller2aba99c2019-02-26 13:15:34 +0000259 iana_data_tar_file = tzdatautil.GetIanaTarFile(iana_input_data_dir, 'tzdata')
Neil Fullercdf56952018-02-20 17:12:37 +0000260 iana_data_version = GetIanaVersion(iana_data_tar_file)
Luca Stefani136579e2019-01-04 17:09:55 +0100261 print('IANA time zone data release %s in %s ...' % (iana_data_version, iana_data_tar_file))
Neil Fullerfa89b032017-06-14 15:58:26 +0100262
263 icu_dir = icuutil.icuDir()
Luca Stefani136579e2019-01-04 17:09:55 +0100264 print('Found icu in %s ...' % icu_dir)
Neil Fuller86e72c52017-06-12 15:36:06 +0100265
Neil Fullercdf56952018-02-20 17:12:37 +0000266 BuildIcuData(iana_data_tar_file)
267
268 iana_tools_dir = '%s/iana' % timezone_input_tools_dir
269 zic_binary_file = BuildZic(iana_tools_dir)
270
271 iana_data_dir = '%s/iana_data' % tmp_dir
272 ExtractTarFile(iana_data_tar_file, iana_data_dir)
273 BuildTzdata(zic_binary_file, iana_data_dir, iana_data_version)
Neil Fullered0c59b2020-05-03 21:18:13 +0100274
Neil Fuller78031492020-08-07 21:17:58 +0100275 BuildTzlookupAndTzIds(iana_data_dir)
Neil Fullered0c59b2020-05-03 21:18:13 +0100276
Neil Fullerf85e8142019-10-28 17:21:55 +0000277 BuildTelephonylookup()
Neil Fullerad7e81c2017-06-15 16:45:56 +0100278
Neil Fullere2d551b2018-10-29 15:32:06 +0000279 # Create a distro file and version file from the output from prior stages.
280 output_distro_dir = '%s/distro' % timezone_output_data_dir
281 output_version_file = '%s/version/tz_version' % timezone_output_data_dir
282 CreateDistroFiles(iana_data_version, output_distro_dir, output_version_file)
Neil Fullerad7e81c2017-06-15 16:45:56 +0100283
Neil Fullerad3c8762017-10-20 15:58:57 +0100284 # Update test versions of distro files too.
285 UpdateTestFiles()
286
Luca Stefani136579e2019-01-04 17:09:55 +0100287 print('Look in %s and %s for new files' % (timezone_output_data_dir, icu_dir))
Neil Fuller86e72c52017-06-12 15:36:06 +0100288 sys.exit(0)
289
290
291if __name__ == '__main__':
292 main()