| #!/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()) |