blob: 71618fde2ce8ae9f8c6fd5ce41c2ed9db096fa53 [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 Fuller8cec5612017-10-11 10:38:17 +0100165 subprocess.check_call(['make', '-C', android_build_top, '-j30', 'zone_compactor'])
Neil Fuller86e72c52017-06-12 15:36:06 +0100166
Neil Fullercdf56952018-02-20 17:12:37 +0000167 # Create args for ZoneCompactor
168 zone_tab_file = '%s/zone.tab' % extracted_iana_data_dir
Neil Fuller8cec5612017-10-11 10:38:17 +0100169 jar_file = '%s/framework/zone_compactor.jar' % android_host_out
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 Fuller35467e12017-06-12 16:55:44 +0100173 iana_output_data_dir = '%s/iana' % timezone_output_data_dir
Neil Fuller8cec5612017-10-11 10:38:17 +0100174 subprocess.check_call(['java', '-jar', jar_file,
Neil Fuller86e72c52017-06-12 15:36:06 +0100175 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 Fuller8cec5612017-10-11 10:38:17 +0100184 subprocess.check_call(['make', '-C', android_build_top, '-j30', 'tzlookup_generator'])
185
186 jar_file = '%s/framework/tzlookup_generator.jar' % android_host_out
187 zone_tab_file = '%s/zone.tab' % iana_data_dir
188 subprocess.check_call(['java', '-jar', jar_file,
189 countryzones_source_file, zone_tab_file, tzlookup_dest_file])
Neil Fuller56166d32017-06-12 12:57:10 +0100190
Neil Fuller86e72c52017-06-12 15:36:06 +0100191
Neil Fullere2d551b2018-10-29 15:32:06 +0000192def CreateDistroFiles(iana_data_version, output_distro_dir, output_version_file):
Neil Fullerad7e81c2017-06-15 16:45:56 +0100193 create_distro_script = '%s/distro/tools/create-distro.py' % timezone_dir
194
195 tzdata_file = '%s/iana/tzdata' % timezone_output_data_dir
196 icu_file = '%s/icu_overlay/icu_tzdata.dat' % timezone_output_data_dir
197 tzlookup_file = '%s/android/tzlookup.xml' % timezone_output_data_dir
198
Neil Fullere2d551b2018-10-29 15:32:06 +0000199 distro_file_pattern = '%s/*.zip' % output_distro_dir
200 existing_files = glob.glob(distro_file_pattern)
Neil Fullerad7e81c2017-06-15 16:45:56 +0100201
Luca Stefani136579e2019-01-04 17:09:55 +0100202 print('Removing %s' % existing_files)
Neil Fullerad7e81c2017-06-15 16:45:56 +0100203 for existing_file in existing_files:
204 os.remove(existing_file)
205
206 subprocess.check_call([create_distro_script,
Neil Fullercdf56952018-02-20 17:12:37 +0000207 '-iana_version', iana_data_version,
Neil Fullerad7e81c2017-06-15 16:45:56 +0100208 '-tzdata', tzdata_file,
209 '-icu', icu_file,
210 '-tzlookup', tzlookup_file,
Neil Fullere2d551b2018-10-29 15:32:06 +0000211 '-output_distro_dir', output_distro_dir,
212 '-output_version_file', output_version_file])
Neil Fullerad7e81c2017-06-15 16:45:56 +0100213
Neil Fullerad3c8762017-10-20 15:58:57 +0100214def UpdateTestFiles():
215 testing_data_dir = '%s/testing/data' % timezone_dir
216 update_test_files_script = '%s/create-test-data.sh' % testing_data_dir
217 subprocess.check_call([update_test_files_script], cwd=testing_data_dir)
218
Neil Fuller86e72c52017-06-12 15:36:06 +0100219
220# Run with no arguments from any directory, with no special setup required.
221# See http://www.iana.org/time-zones/ for more about the source of this data.
222def main():
Luca Stefani136579e2019-01-04 17:09:55 +0100223 print('Source data file structure: %s' % timezone_input_data_dir)
224 print('Source tools file structure: %s' % timezone_input_tools_dir)
225 print('Output data file structure: %s' % timezone_output_data_dir)
Neil Fuller56166d32017-06-12 12:57:10 +0100226
Neil Fuller8cec5612017-10-11 10:38:17 +0100227 iana_input_data_dir = '%s/iana' % timezone_input_data_dir
Neil Fuller2aba99c2019-02-26 13:15:34 +0000228 iana_data_tar_file = tzdatautil.GetIanaTarFile(iana_input_data_dir, 'tzdata')
Neil Fullercdf56952018-02-20 17:12:37 +0000229 iana_data_version = GetIanaVersion(iana_data_tar_file)
Luca Stefani136579e2019-01-04 17:09:55 +0100230 print('IANA time zone data release %s in %s ...' % (iana_data_version, iana_data_tar_file))
Neil Fullerfa89b032017-06-14 15:58:26 +0100231
232 icu_dir = icuutil.icuDir()
Luca Stefani136579e2019-01-04 17:09:55 +0100233 print('Found icu in %s ...' % icu_dir)
Neil Fuller86e72c52017-06-12 15:36:06 +0100234
Neil Fullercdf56952018-02-20 17:12:37 +0000235 BuildIcuData(iana_data_tar_file)
236
237 iana_tools_dir = '%s/iana' % timezone_input_tools_dir
238 zic_binary_file = BuildZic(iana_tools_dir)
239
240 iana_data_dir = '%s/iana_data' % tmp_dir
241 ExtractTarFile(iana_data_tar_file, iana_data_dir)
242 BuildTzdata(zic_binary_file, iana_data_dir, iana_data_version)
Neil Fuller8cec5612017-10-11 10:38:17 +0100243 BuildTzlookup(iana_data_dir)
Neil Fullerad7e81c2017-06-15 16:45:56 +0100244
Neil Fullere2d551b2018-10-29 15:32:06 +0000245 # Create a distro file and version file from the output from prior stages.
246 output_distro_dir = '%s/distro' % timezone_output_data_dir
247 output_version_file = '%s/version/tz_version' % timezone_output_data_dir
248 CreateDistroFiles(iana_data_version, output_distro_dir, output_version_file)
Neil Fullerad7e81c2017-06-15 16:45:56 +0100249
Neil Fullerad3c8762017-10-20 15:58:57 +0100250 # Update test versions of distro files too.
251 UpdateTestFiles()
252
Luca Stefani136579e2019-01-04 17:09:55 +0100253 print('Look in %s and %s for new files' % (timezone_output_data_dir, icu_dir))
Neil Fuller86e72c52017-06-12 15:36:06 +0100254 sys.exit(0)
255
256
257if __name__ == '__main__':
258 main()