|  | #!/usr/bin/env python | 
|  | # Copyright (c) 2016 The Chromium Authors. All rights reserved. | 
|  | # Use of this source code is governed by a BSD-style license that can be | 
|  | # found in the LICENSE file. | 
|  | """Writes a file that contains a define that approximates the build date. | 
|  |  | 
|  | build_type impacts the timestamp generated: | 
|  | - default: the build date is set to the most recent first Sunday of a month at | 
|  | 5:00am. The reason is that it is a time where invalidating the build cache | 
|  | shouldn't have major reprecussions (due to lower load). | 
|  | - official: the build date is set to the current date at 5:00am, or the day | 
|  | before if the current time is before 5:00am. | 
|  | Either way, it is guaranteed to be in the past and always in UTC. | 
|  |  | 
|  | It is also possible to explicitly set a build date to be used. | 
|  | """ | 
|  |  | 
|  | import argparse | 
|  | import calendar | 
|  | import datetime | 
|  | import doctest | 
|  | import os | 
|  | import sys | 
|  |  | 
|  |  | 
|  | def GetFirstSundayOfMonth(year, month): | 
|  | """Returns the first sunday of the given month of the given year. | 
|  |  | 
|  | >>> GetFirstSundayOfMonth(2016, 2) | 
|  | 7 | 
|  | >>> GetFirstSundayOfMonth(2016, 3) | 
|  | 6 | 
|  | >>> GetFirstSundayOfMonth(2000, 1) | 
|  | 2 | 
|  | """ | 
|  | weeks = calendar.Calendar().monthdays2calendar(year, month) | 
|  | # Return the first day in the first week that is a Sunday. | 
|  | return [date_day[0] for date_day in weeks[0] if date_day[1] == 6][0] | 
|  |  | 
|  |  | 
|  | def GetBuildDate(build_type, utc_now): | 
|  | """Gets the approximate build date given the specific build type. | 
|  |  | 
|  | >>> GetBuildDate('default', datetime.datetime(2016, 2, 6, 1, 2, 3)) | 
|  | 'Jan 03 2016 01:02:03' | 
|  | >>> GetBuildDate('default', datetime.datetime(2016, 2, 7, 5)) | 
|  | 'Feb 07 2016 05:00:00' | 
|  | >>> GetBuildDate('default', datetime.datetime(2016, 2, 8, 5)) | 
|  | 'Feb 07 2016 05:00:00' | 
|  | """ | 
|  | day = utc_now.day | 
|  | month = utc_now.month | 
|  | year = utc_now.year | 
|  | if build_type != 'official': | 
|  | first_sunday = GetFirstSundayOfMonth(year, month) | 
|  | # If our build is after the first Sunday, we've already refreshed our build | 
|  | # cache on a quiet day, so just use that day. | 
|  | # Otherwise, take the first Sunday of the previous month. | 
|  | if day >= first_sunday: | 
|  | day = first_sunday | 
|  | else: | 
|  | month -= 1 | 
|  | if month == 0: | 
|  | month = 12 | 
|  | year -= 1 | 
|  | day = GetFirstSundayOfMonth(year, month) | 
|  | now = datetime.datetime( | 
|  | year, month, day, utc_now.hour, utc_now.minute, utc_now.second) | 
|  | return '{:%b %d %Y %H:%M:%S}'.format(now) | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | if doctest.testmod()[0]: | 
|  | return 1 | 
|  | argument_parser = argparse.ArgumentParser( | 
|  | description=sys.modules[__name__].__doc__, | 
|  | formatter_class=argparse.RawDescriptionHelpFormatter) | 
|  | argument_parser.add_argument('output_file', help='The file to write to') | 
|  | argument_parser.add_argument( | 
|  | 'build_type', help='The type of build', choices=('official', 'default')) | 
|  | argument_parser.add_argument( | 
|  | 'build_date_override', nargs='?', | 
|  | help='Optional override for the build date. Format must be ' | 
|  | '\'Mmm DD YYYY HH:MM:SS\'') | 
|  | args = argument_parser.parse_args() | 
|  |  | 
|  | if args.build_date_override: | 
|  | # Format is expected to be "Mmm DD YYYY HH:MM:SS". | 
|  | build_date = args.build_date_override | 
|  | else: | 
|  | now = datetime.datetime.utcnow() | 
|  | if now.hour < 5: | 
|  | # The time is locked at 5:00 am in UTC to cause the build cache | 
|  | # invalidation to not happen exactly at midnight. Use the same calculation | 
|  | # as the day before. | 
|  | # See //base/build_time.cc. | 
|  | now = now - datetime.timedelta(days=1) | 
|  | now = datetime.datetime(now.year, now.month, now.day, 5, 0, 0) | 
|  | build_date = GetBuildDate(args.build_type, now) | 
|  |  | 
|  | output = ('// Generated by //build/write_build_date_header.py\n' | 
|  | '#ifndef BUILD_DATE\n' | 
|  | '#define BUILD_DATE "{}"\n' | 
|  | '#endif // BUILD_DATE\n'.format(build_date)) | 
|  |  | 
|  | current_contents = '' | 
|  | if os.path.isfile(args.output_file): | 
|  | with open(args.output_file, 'r') as current_file: | 
|  | current_contents = current_file.read() | 
|  |  | 
|  | if current_contents != output: | 
|  | with open(args.output_file, 'w') as output_file: | 
|  | output_file.write(output) | 
|  | return 0 | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | sys.exit(main()) |