blob: 61ab30efc34c91f7e7cdc06a0a1984b106f012e9 [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:
75 if fields[0] == 'Link':
76 links.append('%s %s %s' % (fields[0], fields[1], fields[2]))
77 zones.append(fields[2])
78 elif fields[0] == 'Zone':
79 zones.append(fields[1])
Neil Fuller86e72c52017-06-12 15:36:06 +010080 zones.sort()
81
82 zone_compactor_setup_file = '%s/setup' % tmp_dir
83 setup = open(zone_compactor_setup_file, 'w')
84 for link in sorted(set(links)):
85 setup.write('%s\n' % link)
86 for zone in sorted(set(zones)):
87 setup.write('%s\n' % zone)
88 setup.close()
89 return zone_compactor_setup_file
90
91
Neil Fullercdf56952018-02-20 17:12:37 +000092def BuildIcuData(iana_data_tar_file):
Neil Fuller86e72c52017-06-12 15:36:06 +010093 icu_build_dir = '%s/icu' % tmp_dir
94
Neil Fullerfa89b032017-06-14 15:58:26 +010095 icuutil.PrepareIcuBuild(icu_build_dir)
Neil Fullercdf56952018-02-20 17:12:37 +000096 icuutil.MakeTzDataFiles(icu_build_dir, iana_data_tar_file)
Neil Fullerfa89b032017-06-14 15:58:26 +010097
98 # Create ICU system image files.
99 icuutil.MakeAndCopyIcuDataFiles(icu_build_dir)
100
Neil Fuller64bd4112018-11-01 18:14:07 +0000101 icu_overlay_dir = '%s/icu_overlay' % timezone_output_data_dir
102
Neil Fullerfa89b032017-06-14 15:58:26 +0100103 # Create the ICU overlay time zone file.
Neil Fuller64bd4112018-11-01 18:14:07 +0000104 icu_overlay_dat_file = '%s/icu_tzdata.dat' % icu_overlay_dir
Neil Fullerfa89b032017-06-14 15:58:26 +0100105 icuutil.MakeAndCopyOverlayTzIcuData(icu_build_dir, icu_overlay_dat_file)
Neil Fuller86e72c52017-06-12 15:36:06 +0100106
Neil Fuller64bd4112018-11-01 18:14:07 +0000107 # Copy ICU license file(s)
108 icuutil.CopyLicenseFiles(icu_overlay_dir)
109
Neil Fuller86e72c52017-06-12 15:36:06 +0100110
Neil Fullercdf56952018-02-20 17:12:37 +0000111def GetIanaVersion(iana_tar_file):
112 iana_tar_filename = os.path.basename(iana_tar_file)
113 iana_version = re.search('tz(?:data|code)(.+)\\.tar\\.gz', iana_tar_filename).group(1)
Neil Fullerad7e81c2017-06-15 16:45:56 +0100114 return iana_version
115
Neil Fuller8cec5612017-10-11 10:38:17 +0100116
Neil Fullercdf56952018-02-20 17:12:37 +0000117def ExtractTarFile(tar_file, dir):
Luca Stefani136579e2019-01-04 17:09:55 +0100118 print('Extracting %s...' % tar_file)
Neil Fullercdf56952018-02-20 17:12:37 +0000119 if not os.path.exists(dir):
120 os.mkdir(dir)
121 tar = tarfile.open(tar_file, 'r')
122 tar.extractall(dir)
Neil Fuller86e72c52017-06-12 15:36:06 +0100123
Neil Fullercdf56952018-02-20 17:12:37 +0000124
125def BuildZic(iana_tools_dir):
Neil Fuller2aba99c2019-02-26 13:15:34 +0000126 iana_zic_code_tar_file = tzdatautil.GetIanaTarFile(iana_tools_dir, 'tzcode')
Neil Fullercdf56952018-02-20 17:12:37 +0000127 iana_zic_code_version = GetIanaVersion(iana_zic_code_tar_file)
Neil Fuller2aba99c2019-02-26 13:15:34 +0000128 iana_zic_data_tar_file = tzdatautil.GetIanaTarFile(iana_tools_dir, 'tzdata')
Neil Fullercdf56952018-02-20 17:12:37 +0000129 iana_zic_data_version = GetIanaVersion(iana_zic_data_tar_file)
130
Luca Stefani136579e2019-01-04 17:09:55 +0100131 print('Found IANA zic release %s/%s in %s/%s ...' \
132 % (iana_zic_code_version, iana_zic_data_version, iana_zic_code_tar_file, iana_zic_data_tar_file))
Neil Fullercdf56952018-02-20 17:12:37 +0000133
134 zic_build_dir = '%s/zic' % tmp_dir
135 ExtractTarFile(iana_zic_code_tar_file, zic_build_dir)
136 ExtractTarFile(iana_zic_data_tar_file, zic_build_dir)
137
138 # zic
Luca Stefani136579e2019-01-04 17:09:55 +0100139 print('Building zic...')
Neil Fullercdf56952018-02-20 17:12:37 +0000140 # VERSION_DEPS= is to stop the build process looking for files that might not
141 # be present across different versions.
142 subprocess.check_call(['make', '-C', zic_build_dir, 'zic'])
143
144 zic_binary_file = '%s/zic' % zic_build_dir
145 if not os.path.exists(zic_binary_file):
Luca Stefani136579e2019-01-04 17:09:55 +0100146 print('Could not find %s' % zic_binary_file)
Neil Fullera591fb02018-05-08 17:29:11 +0100147 sys.exit(1)
Neil Fullercdf56952018-02-20 17:12:37 +0000148 return zic_binary_file
149
150
151def BuildTzdata(zic_binary_file, extracted_iana_data_dir, iana_data_version):
Luca Stefani136579e2019-01-04 17:09:55 +0100152 print('Generating zic input file...')
Neil Fullera591fb02018-05-08 17:29:11 +0100153 zic_input_file = GenerateZicInputFile(extracted_iana_data_dir)
154
Luca Stefani136579e2019-01-04 17:09:55 +0100155 print('Calling zic...')
Neil Fuller86e72c52017-06-12 15:36:06 +0100156 zic_output_dir = '%s/data' % tmp_dir
157 os.mkdir(zic_output_dir)
Neil Fullera591fb02018-05-08 17:29:11 +0100158 zic_cmd = [zic_binary_file, '-d', zic_output_dir, zic_input_file]
Neil Fuller86e72c52017-06-12 15:36:06 +0100159 subprocess.check_call(zic_cmd)
160
Neil Fullercdf56952018-02-20 17:12:37 +0000161 # ZoneCompactor
Neil Fullera591fb02018-05-08 17:29:11 +0100162 zone_compactor_setup_file = WriteSetupFile(zic_input_file)
Neil Fuller86e72c52017-06-12 15:36:06 +0100163
Luca Stefani136579e2019-01-04 17:09:55 +0100164 print('Calling ZoneCompactor to update tzdata to %s...' % iana_data_version)
Neil Fullerd322be62019-09-16 16:25:03 +0100165
166 tzdatautil.InvokeSoong(android_build_top, ['zone_compactor'])
Neil Fuller86e72c52017-06-12 15:36:06 +0100167
Neil Fullercdf56952018-02-20 17:12:37 +0000168 # Create args for ZoneCompactor
169 zone_tab_file = '%s/zone.tab' % extracted_iana_data_dir
Neil Fullercdf56952018-02-20 17:12:37 +0000170 header_string = 'tzdata%s' % iana_data_version
Neil Fuller35467e12017-06-12 16:55:44 +0100171
Luca Stefani136579e2019-01-04 17:09:55 +0100172 print('Executing ZoneCompactor...')
Neil Fullerd322be62019-09-16 16:25:03 +0100173 command = '%s/bin/zone_compactor' % android_host_out
Neil Fuller35467e12017-06-12 16:55:44 +0100174 iana_output_data_dir = '%s/iana' % timezone_output_data_dir
Neil Fullerd322be62019-09-16 16:25:03 +0100175 subprocess.check_call([command, zone_compactor_setup_file, zic_output_dir, zone_tab_file,
Neil Fullerad7e81c2017-06-15 16:45:56 +0100176 iana_output_data_dir, header_string])
Neil Fuller56166d32017-06-12 12:57:10 +0100177
178
Neil Fuller8cec5612017-10-11 10:38:17 +0100179def BuildTzlookup(iana_data_dir):
180 countryzones_source_file = '%s/android/countryzones.txt' % timezone_input_data_dir
Neil Fuller35467e12017-06-12 16:55:44 +0100181 tzlookup_dest_file = '%s/android/tzlookup.xml' % timezone_output_data_dir
Neil Fuller8cec5612017-10-11 10:38:17 +0100182
Luca Stefani136579e2019-01-04 17:09:55 +0100183 print('Calling TzLookupGenerator to create tzlookup.xml...')
Neil Fullerd322be62019-09-16 16:25:03 +0100184 tzdatautil.InvokeSoong(android_build_top, ['tzlookup_generator'])
Neil Fuller8cec5612017-10-11 10:38:17 +0100185
Neil Fuller8cec5612017-10-11 10:38:17 +0100186 zone_tab_file = '%s/zone.tab' % iana_data_dir
Neil Fullerd322be62019-09-16 16:25:03 +0100187 command = '%s/bin/tzlookup_generator' % android_host_out
188 subprocess.check_call([command, countryzones_source_file, zone_tab_file, tzlookup_dest_file])
Neil Fuller56166d32017-06-12 12:57:10 +0100189
Neil Fuller86e72c52017-06-12 15:36:06 +0100190
Neil Fullerf85e8142019-10-28 17:21:55 +0000191def BuildTelephonylookup():
192 telephonylookup_source_file = '%s/android/telephonylookup.txt' % timezone_input_data_dir
193 telephonylookup_dest_file = '%s/android/telephonylookup.xml' % timezone_output_data_dir
194
195 print('Calling TelephonyLookupGenerator to create telephonylookup.xml...')
196 tzdatautil.InvokeSoong(android_build_top, ['telephonylookup_generator'])
197
198 command = '%s/bin/telephonylookup_generator' % android_host_out
199 subprocess.check_call([command, telephonylookup_source_file, telephonylookup_dest_file])
200
201
Neil Fullere2d551b2018-10-29 15:32:06 +0000202def CreateDistroFiles(iana_data_version, output_distro_dir, output_version_file):
Neil Fullerad7e81c2017-06-15 16:45:56 +0100203 create_distro_script = '%s/distro/tools/create-distro.py' % timezone_dir
204
205 tzdata_file = '%s/iana/tzdata' % timezone_output_data_dir
206 icu_file = '%s/icu_overlay/icu_tzdata.dat' % timezone_output_data_dir
207 tzlookup_file = '%s/android/tzlookup.xml' % timezone_output_data_dir
Neil Fullerf85e8142019-10-28 17:21:55 +0000208 telephonylookup_file = '%s/android/telephonylookup.xml' % timezone_output_data_dir
Neil Fullerad7e81c2017-06-15 16:45:56 +0100209
Neil Fullere2d551b2018-10-29 15:32:06 +0000210 distro_file_pattern = '%s/*.zip' % output_distro_dir
211 existing_files = glob.glob(distro_file_pattern)
Neil Fullerad7e81c2017-06-15 16:45:56 +0100212
Luca Stefani136579e2019-01-04 17:09:55 +0100213 print('Removing %s' % existing_files)
Neil Fullerad7e81c2017-06-15 16:45:56 +0100214 for existing_file in existing_files:
215 os.remove(existing_file)
216
217 subprocess.check_call([create_distro_script,
Neil Fullercdf56952018-02-20 17:12:37 +0000218 '-iana_version', iana_data_version,
Neil Fullerad7e81c2017-06-15 16:45:56 +0100219 '-tzdata', tzdata_file,
220 '-icu', icu_file,
221 '-tzlookup', tzlookup_file,
Neil Fullerf85e8142019-10-28 17:21:55 +0000222 '-telephonylookup', telephonylookup_file,
Neil Fullere2d551b2018-10-29 15:32:06 +0000223 '-output_distro_dir', output_distro_dir,
224 '-output_version_file', output_version_file])
Neil Fullerad7e81c2017-06-15 16:45:56 +0100225
Neil Fullerad3c8762017-10-20 15:58:57 +0100226def UpdateTestFiles():
227 testing_data_dir = '%s/testing/data' % timezone_dir
228 update_test_files_script = '%s/create-test-data.sh' % testing_data_dir
229 subprocess.check_call([update_test_files_script], cwd=testing_data_dir)
230
Neil Fuller86e72c52017-06-12 15:36:06 +0100231
232# Run with no arguments from any directory, with no special setup required.
233# See http://www.iana.org/time-zones/ for more about the source of this data.
234def main():
Luca Stefani136579e2019-01-04 17:09:55 +0100235 print('Source data file structure: %s' % timezone_input_data_dir)
236 print('Source tools file structure: %s' % timezone_input_tools_dir)
237 print('Output data file structure: %s' % timezone_output_data_dir)
Neil Fuller56166d32017-06-12 12:57:10 +0100238
Neil Fuller8cec5612017-10-11 10:38:17 +0100239 iana_input_data_dir = '%s/iana' % timezone_input_data_dir
Neil Fuller2aba99c2019-02-26 13:15:34 +0000240 iana_data_tar_file = tzdatautil.GetIanaTarFile(iana_input_data_dir, 'tzdata')
Neil Fullercdf56952018-02-20 17:12:37 +0000241 iana_data_version = GetIanaVersion(iana_data_tar_file)
Luca Stefani136579e2019-01-04 17:09:55 +0100242 print('IANA time zone data release %s in %s ...' % (iana_data_version, iana_data_tar_file))
Neil Fullerfa89b032017-06-14 15:58:26 +0100243
244 icu_dir = icuutil.icuDir()
Luca Stefani136579e2019-01-04 17:09:55 +0100245 print('Found icu in %s ...' % icu_dir)
Neil Fuller86e72c52017-06-12 15:36:06 +0100246
Neil Fullercdf56952018-02-20 17:12:37 +0000247 BuildIcuData(iana_data_tar_file)
248
249 iana_tools_dir = '%s/iana' % timezone_input_tools_dir
250 zic_binary_file = BuildZic(iana_tools_dir)
251
252 iana_data_dir = '%s/iana_data' % tmp_dir
253 ExtractTarFile(iana_data_tar_file, iana_data_dir)
254 BuildTzdata(zic_binary_file, iana_data_dir, iana_data_version)
Neil Fuller8cec5612017-10-11 10:38:17 +0100255 BuildTzlookup(iana_data_dir)
Neil Fullerf85e8142019-10-28 17:21:55 +0000256 BuildTelephonylookup()
Neil Fullerad7e81c2017-06-15 16:45:56 +0100257
Neil Fullere2d551b2018-10-29 15:32:06 +0000258 # Create a distro file and version file from the output from prior stages.
259 output_distro_dir = '%s/distro' % timezone_output_data_dir
260 output_version_file = '%s/version/tz_version' % timezone_output_data_dir
261 CreateDistroFiles(iana_data_version, output_distro_dir, output_version_file)
Neil Fullerad7e81c2017-06-15 16:45:56 +0100262
Neil Fullerad3c8762017-10-20 15:58:57 +0100263 # Update test versions of distro files too.
264 UpdateTestFiles()
265
Luca Stefani136579e2019-01-04 17:09:55 +0100266 print('Look in %s and %s for new files' % (timezone_output_data_dir, icu_dir))
Neil Fuller86e72c52017-06-12 15:36:06 +0100267 sys.exit(0)
268
269
270if __name__ == '__main__':
271 main()