| Elliott Hughes | 5d967e4 | 2012-07-20 16:52:39 -0700 | [diff] [blame] | 1 | #!/usr/bin/python | 
| Elliott Hughes | 5b1497a | 2012-10-19 14:47:37 -0700 | [diff] [blame] | 2 |  | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 3 | """Updates the timezone data held in bionic and ICU.""" | 
| Elliott Hughes | d40e63e | 2011-02-17 16:20:07 -0800 | [diff] [blame] | 4 |  | 
| Elliott Hughes | 5d967e4 | 2012-07-20 16:52:39 -0700 | [diff] [blame] | 5 | import ftplib | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 6 | import glob | 
| Elliott Hughes | f8dff7d | 2013-04-22 11:11:43 -0700 | [diff] [blame] | 7 | import httplib | 
| Elliott Hughes | 5d967e4 | 2012-07-20 16:52:39 -0700 | [diff] [blame] | 8 | import os | 
|  | 9 | import re | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 10 | import shutil | 
| Elliott Hughes | 5d967e4 | 2012-07-20 16:52:39 -0700 | [diff] [blame] | 11 | import subprocess | 
|  | 12 | import sys | 
|  | 13 | import tarfile | 
|  | 14 | import tempfile | 
| Elliott Hughes | d40e63e | 2011-02-17 16:20:07 -0800 | [diff] [blame] | 15 |  | 
| Elliott Hughes | 5b1497a | 2012-10-19 14:47:37 -0700 | [diff] [blame] | 16 | regions = ['africa', 'antarctica', 'asia', 'australasia', 'backward', | 
|  | 17 | 'etcetera', 'europe', 'northamerica', 'southamerica'] | 
| Elliott Hughes | 5d967e4 | 2012-07-20 16:52:39 -0700 | [diff] [blame] | 18 |  | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 19 | def CheckDirExists(dir, dirname): | 
|  | 20 | if not os.path.isdir(dir): | 
|  | 21 | print "Couldn't find %s (%s)!" % (dirname, dir) | 
|  | 22 | sys.exit(1) | 
|  | 23 |  | 
|  | 24 | bionic_libc_tools_zoneinfo_dir = os.path.realpath(os.path.dirname(sys.argv[0])) | 
|  | 25 |  | 
|  | 26 | # Find the bionic directory, searching upward from this script. | 
|  | 27 | bionic_dir = os.path.realpath('%s/../../..' % bionic_libc_tools_zoneinfo_dir) | 
|  | 28 | bionic_libc_zoneinfo_dir = '%s/libc/zoneinfo' % bionic_dir | 
|  | 29 | CheckDirExists(bionic_libc_zoneinfo_dir, 'bionic/libc/zoneinfo') | 
|  | 30 | CheckDirExists(bionic_libc_tools_zoneinfo_dir, 'bionic/libc/tools/zoneinfo') | 
|  | 31 | print 'Found bionic in %s ...' % bionic_dir | 
|  | 32 |  | 
|  | 33 | # Find the icu4c directory. | 
| Elliott Hughes | 30ab939 | 2014-07-09 15:42:59 -0700 | [diff] [blame] | 34 | icu_dir = os.path.realpath('%s/../external/icu/icu4c/source' % bionic_dir) | 
|  | 35 | CheckDirExists(icu_dir, 'external/icu/icu4c/source') | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 36 | print 'Found icu in %s ...' % icu_dir | 
|  | 37 |  | 
| Elliott Hughes | 5d967e4 | 2012-07-20 16:52:39 -0700 | [diff] [blame] | 38 |  | 
| Elliott Hughes | 5b1497a | 2012-10-19 14:47:37 -0700 | [diff] [blame] | 39 | def GetCurrentTzDataVersion(): | 
| Elliott Hughes | e3063f4 | 2012-11-05 08:53:28 -0800 | [diff] [blame] | 40 | return open('%s/tzdata' % bionic_libc_zoneinfo_dir).read().split('\x00', 1)[0] | 
| Elliott Hughes | 5d967e4 | 2012-07-20 16:52:39 -0700 | [diff] [blame] | 41 |  | 
|  | 42 |  | 
| Elliott Hughes | 5b1497a | 2012-10-19 14:47:37 -0700 | [diff] [blame] | 43 | def WriteSetupFile(): | 
| Elliott Hughes | e3063f4 | 2012-11-05 08:53:28 -0800 | [diff] [blame] | 44 | """Writes the list of zones that ZoneCompactor should process.""" | 
| Elliott Hughes | 5b1497a | 2012-10-19 14:47:37 -0700 | [diff] [blame] | 45 | links = [] | 
|  | 46 | zones = [] | 
|  | 47 | for region in regions: | 
|  | 48 | for line in open('extracted/%s' % region): | 
|  | 49 | fields = line.split() | 
| Elliott Hughes | e3063f4 | 2012-11-05 08:53:28 -0800 | [diff] [blame] | 50 | if fields: | 
|  | 51 | if fields[0] == 'Link': | 
|  | 52 | links.append('%s %s %s\n' % (fields[0], fields[1], fields[2])) | 
|  | 53 | zones.append(fields[2]) | 
|  | 54 | elif fields[0] == 'Zone': | 
|  | 55 | zones.append(fields[1]) | 
| Elliott Hughes | 5b1497a | 2012-10-19 14:47:37 -0700 | [diff] [blame] | 56 | zones.sort() | 
|  | 57 |  | 
|  | 58 | setup = open('setup', 'w') | 
|  | 59 | for link in links: | 
|  | 60 | setup.write(link) | 
|  | 61 | for zone in zones: | 
|  | 62 | setup.write('%s\n' % zone) | 
|  | 63 | setup.close() | 
| Elliott Hughes | 5d967e4 | 2012-07-20 16:52:39 -0700 | [diff] [blame] | 64 |  | 
|  | 65 |  | 
| Elliott Hughes | f8dff7d | 2013-04-22 11:11:43 -0700 | [diff] [blame] | 66 | def SwitchToNewTemporaryDirectory(): | 
| Elliott Hughes | 5d967e4 | 2012-07-20 16:52:39 -0700 | [diff] [blame] | 67 | tmp_dir = tempfile.mkdtemp('-tzdata') | 
|  | 68 | os.chdir(tmp_dir) | 
|  | 69 | print 'Created temporary directory "%s"...' % tmp_dir | 
|  | 70 |  | 
| Elliott Hughes | f8dff7d | 2013-04-22 11:11:43 -0700 | [diff] [blame] | 71 |  | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 72 | def FtpRetrieveFile(ftp, filename): | 
| Elliott Hughes | f8dff7d | 2013-04-22 11:11:43 -0700 | [diff] [blame] | 73 | ftp.retrbinary('RETR %s' % filename, open(filename, 'wb').write) | 
|  | 74 |  | 
|  | 75 |  | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 76 | def FtpRetrieveFileAndSignature(ftp, data_filename): | 
| Elliott Hughes | f8dff7d | 2013-04-22 11:11:43 -0700 | [diff] [blame] | 77 | """Downloads and repackages the given data from the given FTP server.""" | 
| Elliott Hughes | 5d2ef87 | 2012-11-26 13:44:49 -0800 | [diff] [blame] | 78 | print 'Downloading data...' | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 79 | FtpRetrieveFile(ftp, data_filename) | 
| Elliott Hughes | 5d2ef87 | 2012-11-26 13:44:49 -0800 | [diff] [blame] | 80 |  | 
|  | 81 | print 'Downloading signature...' | 
|  | 82 | signature_filename = '%s.asc' % data_filename | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 83 | FtpRetrieveFile(ftp, signature_filename) | 
| Elliott Hughes | f8dff7d | 2013-04-22 11:11:43 -0700 | [diff] [blame] | 84 |  | 
|  | 85 |  | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 86 | def HttpRetrieveFile(http, path, output_filename): | 
| Elliott Hughes | 676e66d | 2013-04-22 11:41:57 -0700 | [diff] [blame] | 87 | http.request("GET", path) | 
|  | 88 | f = open(output_filename, 'wb') | 
|  | 89 | f.write(http.getresponse().read()) | 
|  | 90 | f.close() | 
|  | 91 |  | 
|  | 92 |  | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 93 | def HttpRetrieveFileAndSignature(http, data_filename): | 
| Elliott Hughes | f8dff7d | 2013-04-22 11:11:43 -0700 | [diff] [blame] | 94 | """Downloads and repackages the given data from the given HTTP server.""" | 
| Elliott Hughes | 676e66d | 2013-04-22 11:41:57 -0700 | [diff] [blame] | 95 | path = "/time-zones/repository/releases/%s" % data_filename | 
|  | 96 |  | 
| Elliott Hughes | f8dff7d | 2013-04-22 11:11:43 -0700 | [diff] [blame] | 97 | print 'Downloading data...' | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 98 | HttpRetrieveFile(http, path, data_filename) | 
| Elliott Hughes | 676e66d | 2013-04-22 11:41:57 -0700 | [diff] [blame] | 99 |  | 
|  | 100 | print 'Downloading signature...' | 
|  | 101 | signature_filename = '%s.asc' % data_filename | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 102 | HttpRetrievefile(http, "%s.asc" % path, signature_filename) | 
| Elliott Hughes | f8dff7d | 2013-04-22 11:11:43 -0700 | [diff] [blame] | 103 |  | 
|  | 104 |  | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 105 | def BuildIcuToolsAndData(data_filename): | 
|  | 106 | # Keep track of the original cwd so we can go back to it at the end. | 
|  | 107 | original_working_dir = os.getcwd() | 
| Elliott Hughes | f8dff7d | 2013-04-22 11:11:43 -0700 | [diff] [blame] | 108 |  | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 109 | # Create a directory to run 'make' from. | 
|  | 110 | icu_working_dir = '%s/icu' % original_working_dir | 
|  | 111 | os.mkdir(icu_working_dir) | 
|  | 112 | os.chdir(icu_working_dir) | 
|  | 113 |  | 
|  | 114 | # Build the ICU tools. | 
|  | 115 | print 'Configuring ICU tools...' | 
|  | 116 | subprocess.check_call(['%s/runConfigureICU' % icu_dir, 'Linux']) | 
|  | 117 | print 'Making ICU tools...' | 
| Elliott Hughes | 13bab43 | 2014-08-06 15:23:11 -0700 | [diff] [blame] | 118 | subprocess.check_call(['make', '-j32']) | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 119 |  | 
|  | 120 | # Run the ICU tools. | 
|  | 121 | os.chdir('tools/tzcode') | 
|  | 122 | shutil.copyfile('%s/%s' % (original_working_dir, data_filename), data_filename) | 
|  | 123 | print 'Making ICU data...' | 
|  | 124 | subprocess.check_call(['make']) | 
|  | 125 |  | 
| Elliott Hughes | f8896c6 | 2014-09-30 17:30:01 -0700 | [diff] [blame] | 126 | # Copy the source file to its ultimate destination. | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 127 | icu_txt_data_dir = '%s/data/misc' % icu_dir | 
|  | 128 | print 'Copying zoneinfo64.txt to %s ...' % icu_txt_data_dir | 
|  | 129 | shutil.copy('zoneinfo64.txt', icu_txt_data_dir) | 
|  | 130 |  | 
| Elliott Hughes | f8896c6 | 2014-09-30 17:30:01 -0700 | [diff] [blame] | 131 | # Regenerate the .dat file. | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 132 | os.chdir(icu_working_dir) | 
| Elliott Hughes | f8896c6 | 2014-09-30 17:30:01 -0700 | [diff] [blame] | 133 | subprocess.check_call(['make', '-j32']) | 
|  | 134 |  | 
|  | 135 | # Copy the .dat file to its ultimate destination. | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 136 | icu_dat_data_dir = '%s/stubdata' % icu_dir | 
| Neil Fuller | 43f3715 | 2014-05-21 16:59:09 +0100 | [diff] [blame] | 137 | datfiles = glob.glob('data/out/tmp/icudt??l.dat') | 
|  | 138 | if len(datfiles) != 1: | 
|  | 139 | print 'ERROR: Unexpectedly found %d .dat files (%s). Halting.' % (len(datfiles), datfiles) | 
|  | 140 | sys.exit(1) | 
| Neil Fuller | 43f3715 | 2014-05-21 16:59:09 +0100 | [diff] [blame] | 141 | datfile = datfiles[0] | 
|  | 142 | print 'Copying %s to %s ...' % (datfile, icu_dat_data_dir) | 
|  | 143 | shutil.copy(datfile, icu_dat_data_dir) | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 144 |  | 
|  | 145 | # Switch back to the original working cwd. | 
|  | 146 | os.chdir(original_working_dir) | 
|  | 147 |  | 
|  | 148 |  | 
|  | 149 | def CheckSignature(data_filename): | 
| Elliott Hughes | 676e66d | 2013-04-22 11:41:57 -0700 | [diff] [blame] | 150 | signature_filename = '%s.asc' % data_filename | 
|  | 151 | print 'Verifying signature...' | 
|  | 152 | # If this fails for you, you probably need to import Paul Eggert's public key: | 
|  | 153 | # gpg --recv-keys ED97E90E62AA7E34 | 
|  | 154 | subprocess.check_call(['gpg', '--trusted-key=ED97E90E62AA7E34', '--verify', | 
|  | 155 | signature_filename, data_filename]) | 
|  | 156 |  | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 157 |  | 
|  | 158 | def BuildBionicToolsAndData(data_filename): | 
|  | 159 | new_version = re.search('(tzdata.+)\\.tar\\.gz', data_filename).group(1) | 
|  | 160 |  | 
| Elliott Hughes | 5d967e4 | 2012-07-20 16:52:39 -0700 | [diff] [blame] | 161 | print 'Extracting...' | 
|  | 162 | os.mkdir('extracted') | 
| Elliott Hughes | e3063f4 | 2012-11-05 08:53:28 -0800 | [diff] [blame] | 163 | tar = tarfile.open(data_filename, 'r') | 
| Elliott Hughes | 5d967e4 | 2012-07-20 16:52:39 -0700 | [diff] [blame] | 164 | tar.extractall('extracted') | 
|  | 165 |  | 
|  | 166 | print 'Calling zic(1)...' | 
|  | 167 | os.mkdir('data') | 
|  | 168 | for region in regions: | 
|  | 169 | if region != 'backward': | 
|  | 170 | subprocess.check_call(['zic', '-d', 'data', 'extracted/%s' % region]) | 
|  | 171 |  | 
| Elliott Hughes | 5b1497a | 2012-10-19 14:47:37 -0700 | [diff] [blame] | 172 | WriteSetupFile() | 
| Elliott Hughes | 5d967e4 | 2012-07-20 16:52:39 -0700 | [diff] [blame] | 173 |  | 
| Elliott Hughes | 5b1497a | 2012-10-19 14:47:37 -0700 | [diff] [blame] | 174 | print 'Calling ZoneCompactor to update bionic to %s...' % new_version | 
| Elliott Hughes | 5d967e4 | 2012-07-20 16:52:39 -0700 | [diff] [blame] | 175 | subprocess.check_call(['javac', '-d', '.', | 
| Elliott Hughes | 13bab43 | 2014-08-06 15:23:11 -0700 | [diff] [blame] | 176 | '%s/ZoneCompactor.java' % bionic_libc_tools_zoneinfo_dir]) | 
| Elliott Hughes | 5b1497a | 2012-10-19 14:47:37 -0700 | [diff] [blame] | 177 | subprocess.check_call(['java', 'ZoneCompactor', | 
| Elliott Hughes | 2393535 | 2012-10-22 14:47:58 -0700 | [diff] [blame] | 178 | 'setup', 'data', 'extracted/zone.tab', | 
|  | 179 | bionic_libc_zoneinfo_dir, new_version]) | 
| Elliott Hughes | 5d967e4 | 2012-07-20 16:52:39 -0700 | [diff] [blame] | 180 |  | 
| Elliott Hughes | d40e63e | 2011-02-17 16:20:07 -0800 | [diff] [blame] | 181 |  | 
| Elliott Hughes | 5b1497a | 2012-10-19 14:47:37 -0700 | [diff] [blame] | 182 | # Run with no arguments from any directory, with no special setup required. | 
| Elliott Hughes | e3063f4 | 2012-11-05 08:53:28 -0800 | [diff] [blame] | 183 | # See http://www.iana.org/time-zones/ for more about the source of this data. | 
| Elliott Hughes | 5b1497a | 2012-10-19 14:47:37 -0700 | [diff] [blame] | 184 | def main(): | 
| Elliott Hughes | 5b1497a | 2012-10-19 14:47:37 -0700 | [diff] [blame] | 185 | print 'Looking for new tzdata...' | 
| Elliott Hughes | f8dff7d | 2013-04-22 11:11:43 -0700 | [diff] [blame] | 186 |  | 
| Elliott Hughes | 5b1497a | 2012-10-19 14:47:37 -0700 | [diff] [blame] | 187 | tzdata_filenames = [] | 
| Elliott Hughes | f8dff7d | 2013-04-22 11:11:43 -0700 | [diff] [blame] | 188 |  | 
|  | 189 | # The FTP server lets you download intermediate releases, and also lets you | 
| Elliott Hughes | 21da42e | 2013-04-22 13:44:50 -0700 | [diff] [blame] | 190 | # download the signatures for verification, so it's your best choice. | 
| Elliott Hughes | f8dff7d | 2013-04-22 11:11:43 -0700 | [diff] [blame] | 191 | use_ftp = True | 
|  | 192 |  | 
|  | 193 | if use_ftp: | 
|  | 194 | ftp = ftplib.FTP('ftp.iana.org') | 
|  | 195 | ftp.login() | 
|  | 196 | ftp.cwd('tz/releases') | 
|  | 197 | for filename in ftp.nlst(): | 
|  | 198 | if filename.startswith('tzdata20') and filename.endswith('.tar.gz'): | 
|  | 199 | tzdata_filenames.append(filename) | 
|  | 200 | tzdata_filenames.sort() | 
|  | 201 | else: | 
|  | 202 | http = httplib.HTTPConnection('www.iana.org') | 
|  | 203 | http.request("GET", "/time-zones") | 
|  | 204 | index_lines = http.getresponse().read().split('\n') | 
|  | 205 | for line in index_lines: | 
|  | 206 | m = re.compile('.*href="/time-zones/repository/releases/(tzdata20\d\d\c\.tar\.gz)".*').match(line) | 
|  | 207 | if m: | 
|  | 208 | tzdata_filenames.append(m.group(1)) | 
| Elliott Hughes | bcb2eda | 2011-10-24 10:47:25 -0700 | [diff] [blame] | 209 |  | 
| Elliott Hughes | 5b1497a | 2012-10-19 14:47:37 -0700 | [diff] [blame] | 210 | # If you're several releases behind, we'll walk you through the upgrades | 
|  | 211 | # one by one. | 
|  | 212 | current_version = GetCurrentTzDataVersion() | 
|  | 213 | current_filename = '%s.tar.gz' % current_version | 
|  | 214 | for filename in tzdata_filenames: | 
|  | 215 | if filename > current_filename: | 
|  | 216 | print 'Found new tzdata: %s' % filename | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 217 | SwitchToNewTemporaryDirectory() | 
| Elliott Hughes | f8dff7d | 2013-04-22 11:11:43 -0700 | [diff] [blame] | 218 | if use_ftp: | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 219 | FtpRetrieveFileAndSignature(ftp, filename) | 
| Elliott Hughes | f8dff7d | 2013-04-22 11:11:43 -0700 | [diff] [blame] | 220 | else: | 
| Neil Fuller | 246c688 | 2014-05-16 18:04:48 +0100 | [diff] [blame] | 221 | HttpRetrieveFileAndSignature(http, filename) | 
|  | 222 |  | 
|  | 223 | CheckSignature(filename) | 
|  | 224 | BuildIcuToolsAndData(filename) | 
|  | 225 | BuildBionicToolsAndData(filename) | 
|  | 226 | print 'Look in %s and %s for new data files' % (bionic_dir, icu_dir) | 
| Elliott Hughes | 5b1497a | 2012-10-19 14:47:37 -0700 | [diff] [blame] | 227 | sys.exit(0) | 
| Elliott Hughes | d40e63e | 2011-02-17 16:20:07 -0800 | [diff] [blame] | 228 |  | 
| Elliott Hughes | 5b1497a | 2012-10-19 14:47:37 -0700 | [diff] [blame] | 229 | print 'You already have the latest tzdata (%s)!' % current_version | 
|  | 230 | sys.exit(0) | 
|  | 231 |  | 
|  | 232 |  | 
|  | 233 | if __name__ == '__main__': | 
|  | 234 | main() |