1cb93a386Sopenharmony_ci#!/usr/bin/env python
2cb93a386Sopenharmony_ci#
3cb93a386Sopenharmony_ci# Copyright 2016 Google Inc.
4cb93a386Sopenharmony_ci#
5cb93a386Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be
6cb93a386Sopenharmony_ci# found in the LICENSE file.
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci
9cb93a386Sopenharmony_ci"""Create the SKP asset."""
10cb93a386Sopenharmony_ci
11cb93a386Sopenharmony_ci
12cb93a386Sopenharmony_cifrom __future__ import print_function
13cb93a386Sopenharmony_ciimport argparse
14cb93a386Sopenharmony_cifrom distutils.dir_util import copy_tree
15cb93a386Sopenharmony_ciimport os
16cb93a386Sopenharmony_ciimport shutil
17cb93a386Sopenharmony_ciimport subprocess
18cb93a386Sopenharmony_ciimport sys
19cb93a386Sopenharmony_ciimport tempfile
20cb93a386Sopenharmony_ci
21cb93a386Sopenharmony_ciFILE_DIR = os.path.dirname(os.path.abspath(__file__))
22cb93a386Sopenharmony_ciINFRA_BOTS_DIR = os.path.realpath(os.path.join(FILE_DIR, os.pardir, os.pardir))
23cb93a386Sopenharmony_cisys.path.insert(0, INFRA_BOTS_DIR)
24cb93a386Sopenharmony_ciimport utils
25cb93a386Sopenharmony_ci
26cb93a386Sopenharmony_ci
27cb93a386Sopenharmony_ciBROWSER_EXECUTABLE_ENV_VAR = 'SKP_BROWSER_EXECUTABLE'
28cb93a386Sopenharmony_ciCHROME_SRC_PATH_ENV_VAR = 'SKP_CHROME_SRC_PATH'
29cb93a386Sopenharmony_ciUPLOAD_TO_PARTNER_BUCKET_ENV_VAR = 'SKP_UPLOAD_TO_PARTNER_BUCKET'
30cb93a386Sopenharmony_ciDM_PATH_ENV_VAR = 'DM_PATH'
31cb93a386Sopenharmony_ci
32cb93a386Sopenharmony_ciSKIA_TOOLS = os.path.join(INFRA_BOTS_DIR, os.pardir, os.pardir, 'tools')
33cb93a386Sopenharmony_ciPRIVATE_SKPS_GS = 'gs://skia-skps/private/skps'
34cb93a386Sopenharmony_ci
35cb93a386Sopenharmony_ci
36cb93a386Sopenharmony_cidef getenv(key):
37cb93a386Sopenharmony_ci  val = os.environ.get(key)
38cb93a386Sopenharmony_ci  if not val:
39cb93a386Sopenharmony_ci    print(('Environment variable %s not set; you should run this via '
40cb93a386Sopenharmony_ci           'create_and_upload.py.' % key), file=sys.stderr)
41cb93a386Sopenharmony_ci    sys.exit(1)
42cb93a386Sopenharmony_ci  return val
43cb93a386Sopenharmony_ci
44cb93a386Sopenharmony_ci
45cb93a386Sopenharmony_cidef get_flutter_skps(target_dir):
46cb93a386Sopenharmony_ci  """Creates SKPs using Flutter's skp_generator tool.
47cb93a386Sopenharmony_ci
48cb93a386Sopenharmony_ci  Documentation is at https://github.com/flutter/tests/tree/master/skp_generator
49cb93a386Sopenharmony_ci  """
50cb93a386Sopenharmony_ci  with utils.tmp_dir():
51cb93a386Sopenharmony_ci    print('Retrieving Flutter SKPs...')
52cb93a386Sopenharmony_ci    utils.git_clone('https://github.com/flutter/tests.git', '.')
53cb93a386Sopenharmony_ci    os.chdir('skp_generator')
54cb93a386Sopenharmony_ci    subprocess.check_call(['bash', 'build.sh'])
55cb93a386Sopenharmony_ci    # Fix invalid SKP file names.
56cb93a386Sopenharmony_ci    for f in os.listdir('skps'):
57cb93a386Sopenharmony_ci      original_file_name = os.path.splitext(f)[0]
58cb93a386Sopenharmony_ci      new_file_name = ''.join([x if x.isalnum() else "_"
59cb93a386Sopenharmony_ci                               for x in original_file_name])
60cb93a386Sopenharmony_ci      if new_file_name != original_file_name:
61cb93a386Sopenharmony_ci        os.rename(os.path.join('skps', f),
62cb93a386Sopenharmony_ci                  os.path.join('skps', new_file_name + '.skp'))
63cb93a386Sopenharmony_ci    copy_tree('skps', target_dir)
64cb93a386Sopenharmony_ci    print('Done retrieving Flutter SKPs.')
65cb93a386Sopenharmony_ci
66cb93a386Sopenharmony_ci
67cb93a386Sopenharmony_cidef create_asset(chrome_src_path, browser_executable, target_dir,
68cb93a386Sopenharmony_ci                 upload_to_partner_bucket, dm_path):
69cb93a386Sopenharmony_ci  """Create the SKP asset.
70cb93a386Sopenharmony_ci
71cb93a386Sopenharmony_ci  Creates the asset from 3 sources:
72cb93a386Sopenharmony_ci  1. From Flutter's skp_generator tool.
73cb93a386Sopenharmony_ci  2. The web pages defined in the tools/skp/page_sets/ directory.
74cb93a386Sopenharmony_ci  3. Any private SKPs stored in $PRIVATE_SKPS_GS after running dm on
75cb93a386Sopenharmony_ci     them (see below).
76cb93a386Sopenharmony_ci
77cb93a386Sopenharmony_ci  The script runs the following cmd on the non-generated SKPs stored in
78cb93a386Sopenharmony_ci  $PRIVATE_SKPS_GS -
79cb93a386Sopenharmony_ci  `dm --config skp -w newskps/ --skps oldskps/ --src skp`
80cb93a386Sopenharmony_ci  The cmd updates the version stored in the SKPs so that the versions in
81cb93a386Sopenharmony_ci  them do not eventually become unsupported.
82cb93a386Sopenharmony_ci  """
83cb93a386Sopenharmony_ci  browser_executable = os.path.realpath(browser_executable)
84cb93a386Sopenharmony_ci  chrome_src_path = os.path.realpath(chrome_src_path)
85cb93a386Sopenharmony_ci  dm_path = os.path.realpath(dm_path)
86cb93a386Sopenharmony_ci  target_dir = os.path.realpath(target_dir)
87cb93a386Sopenharmony_ci
88cb93a386Sopenharmony_ci  if not os.path.exists(target_dir):
89cb93a386Sopenharmony_ci    os.makedirs(target_dir)
90cb93a386Sopenharmony_ci
91cb93a386Sopenharmony_ci  # 1. Flutter SKPs
92cb93a386Sopenharmony_ci  get_flutter_skps(target_dir)
93cb93a386Sopenharmony_ci
94cb93a386Sopenharmony_ci  # 2. Skia's SKPs from tools/skp/page_sets/
95cb93a386Sopenharmony_ci  with utils.tmp_dir():
96cb93a386Sopenharmony_ci    if os.environ.get('CHROME_HEADLESS'):
97cb93a386Sopenharmony_ci      print('Starting xvfb')
98cb93a386Sopenharmony_ci      # Start Xvfb if running on a bot.
99cb93a386Sopenharmony_ci      try:
100cb93a386Sopenharmony_ci        xvfb_proc = subprocess.Popen([
101cb93a386Sopenharmony_ci            'sudo', 'Xvfb', ':0', '-screen', '0', '1280x1024x24'])
102cb93a386Sopenharmony_ci      except Exception:
103cb93a386Sopenharmony_ci        # It is ok if the above command fails, it just means that DISPLAY=:0
104cb93a386Sopenharmony_ci        # is already up.
105cb93a386Sopenharmony_ci        xvfb_proc = None
106cb93a386Sopenharmony_ci
107cb93a386Sopenharmony_ci    print('Running webpages_playback to generate SKPs...')
108cb93a386Sopenharmony_ci    webpages_playback_cmd = [
109cb93a386Sopenharmony_ci      'python', '-u', os.path.join(SKIA_TOOLS, 'skp', 'webpages_playback.py'),
110cb93a386Sopenharmony_ci      '--page_sets', 'all',
111cb93a386Sopenharmony_ci      '--browser_executable', browser_executable,
112cb93a386Sopenharmony_ci      '--non-interactive',
113cb93a386Sopenharmony_ci      '--output_dir', os.getcwd(),
114cb93a386Sopenharmony_ci      '--chrome_src_path', chrome_src_path,
115cb93a386Sopenharmony_ci    ]
116cb93a386Sopenharmony_ci    if upload_to_partner_bucket:
117cb93a386Sopenharmony_ci      webpages_playback_cmd.append('--upload_to_partner_bucket')
118cb93a386Sopenharmony_ci    print('Running webpages_playback command:\n$ %s' %
119cb93a386Sopenharmony_ci        ' '.join(webpages_playback_cmd))
120cb93a386Sopenharmony_ci    try:
121cb93a386Sopenharmony_ci      subprocess.check_call(webpages_playback_cmd)
122cb93a386Sopenharmony_ci    finally:
123cb93a386Sopenharmony_ci      if xvfb_proc:
124cb93a386Sopenharmony_ci        try:
125cb93a386Sopenharmony_ci          xvfb_proc.kill()
126cb93a386Sopenharmony_ci        except OSError as e:
127cb93a386Sopenharmony_ci          print('Failed to kill xvfb process via Popen.kill();'
128cb93a386Sopenharmony_ci                ' attempting `sudo kill`...')
129cb93a386Sopenharmony_ci          try:
130cb93a386Sopenharmony_ci            subprocess.check_call(['sudo', 'kill', '-9', str(xvfb_proc.pid)])
131cb93a386Sopenharmony_ci          except subprocess.CalledProcessError as e:
132cb93a386Sopenharmony_ci            print('Failed to kill xvfb process via `sudo kill`;'
133cb93a386Sopenharmony_ci                  'this may cause a hang.')
134cb93a386Sopenharmony_ci      # Clean up any leftover browser instances. This can happen if there are
135cb93a386Sopenharmony_ci      # telemetry crashes, processes are not always cleaned up appropriately by
136cb93a386Sopenharmony_ci      # the webpagereplay and telemetry frameworks.
137cb93a386Sopenharmony_ci      procs = subprocess.check_output(['ps', 'ax']).decode()
138cb93a386Sopenharmony_ci      for line in procs.splitlines():
139cb93a386Sopenharmony_ci        if browser_executable in line:
140cb93a386Sopenharmony_ci          print(line)
141cb93a386Sopenharmony_ci          pid = line.strip().split(' ')[0]
142cb93a386Sopenharmony_ci          if pid != str(os.getpid()) and not 'python' in line:
143cb93a386Sopenharmony_ci            print('Kill browser process %s' % str(pid))
144cb93a386Sopenharmony_ci            try:
145cb93a386Sopenharmony_ci              subprocess.check_call(['kill', '-9', str(pid)])
146cb93a386Sopenharmony_ci            except subprocess.CalledProcessError as e:
147cb93a386Sopenharmony_ci              print(e)
148cb93a386Sopenharmony_ci          else:
149cb93a386Sopenharmony_ci            pass
150cb93a386Sopenharmony_ci        if 'Xvfb' in line:
151cb93a386Sopenharmony_ci          print(line)
152cb93a386Sopenharmony_ci          pid = line.strip().split(' ')[0]
153cb93a386Sopenharmony_ci          print('Kill Xvfb process %s' % str(pid))
154cb93a386Sopenharmony_ci          try:
155cb93a386Sopenharmony_ci            subprocess.check_call(['sudo', 'kill', '-9', str(pid)])
156cb93a386Sopenharmony_ci          except subprocess.CalledProcessError as e:
157cb93a386Sopenharmony_ci            print(e)
158cb93a386Sopenharmony_ci
159cb93a386Sopenharmony_ci    src = os.path.join(os.getcwd(), 'playback', 'skps')
160cb93a386Sopenharmony_ci    for f in os.listdir(src):
161cb93a386Sopenharmony_ci      if f.endswith('.skp'):
162cb93a386Sopenharmony_ci        shutil.copyfile(os.path.join(src, f), os.path.join(target_dir, f))
163cb93a386Sopenharmony_ci    print('Done running webpages_playback.')
164cb93a386Sopenharmony_ci
165cb93a386Sopenharmony_ci  # 3. Copy over private SKPs from Google storage into the target_dir.
166cb93a386Sopenharmony_ci  old_skps_dir = tempfile.mkdtemp()
167cb93a386Sopenharmony_ci  new_skps_dir = tempfile.mkdtemp()
168cb93a386Sopenharmony_ci  print('Copying non-generated SKPs from private GCS bucket...')
169cb93a386Sopenharmony_ci  subprocess.check_call([
170cb93a386Sopenharmony_ci    'gsutil', 'cp', os.path.join(PRIVATE_SKPS_GS, '*'), old_skps_dir])
171cb93a386Sopenharmony_ci  print('Updating non-generated SKP versions')
172cb93a386Sopenharmony_ci  subprocess.check_call([
173cb93a386Sopenharmony_ci      dm_path,
174cb93a386Sopenharmony_ci      '--config', 'skp',
175cb93a386Sopenharmony_ci      '-w', new_skps_dir,
176cb93a386Sopenharmony_ci      '--skps', old_skps_dir,
177cb93a386Sopenharmony_ci      '--src', 'skp'])
178cb93a386Sopenharmony_ci  for f in os.listdir(new_skps_dir):
179cb93a386Sopenharmony_ci    if f.endswith('.skp'):
180cb93a386Sopenharmony_ci      shutil.copyfile(
181cb93a386Sopenharmony_ci          os.path.join(new_skps_dir, f), os.path.join(target_dir, f))
182cb93a386Sopenharmony_ci  shutil.rmtree(old_skps_dir)
183cb93a386Sopenharmony_ci  shutil.rmtree(new_skps_dir)
184cb93a386Sopenharmony_ci
185cb93a386Sopenharmony_ci
186cb93a386Sopenharmony_cidef main():
187cb93a386Sopenharmony_ci  parser = argparse.ArgumentParser()
188cb93a386Sopenharmony_ci  parser.add_argument('--target_dir', '-t', required=True)
189cb93a386Sopenharmony_ci  args = parser.parse_args()
190cb93a386Sopenharmony_ci
191cb93a386Sopenharmony_ci  # Obtain flags from create_and_upload via environment variables, since
192cb93a386Sopenharmony_ci  # this script is called via `sk` and not directly.
193cb93a386Sopenharmony_ci  chrome_src_path = getenv(CHROME_SRC_PATH_ENV_VAR)
194cb93a386Sopenharmony_ci  browser_executable = getenv(BROWSER_EXECUTABLE_ENV_VAR)
195cb93a386Sopenharmony_ci  upload_to_partner_bucket = getenv(UPLOAD_TO_PARTNER_BUCKET_ENV_VAR) == '1'
196cb93a386Sopenharmony_ci  dm_path = getenv(DM_PATH_ENV_VAR)
197cb93a386Sopenharmony_ci
198cb93a386Sopenharmony_ci  create_asset(chrome_src_path, browser_executable, args.target_dir,
199cb93a386Sopenharmony_ci               upload_to_partner_bucket, dm_path)
200cb93a386Sopenharmony_ci
201cb93a386Sopenharmony_ci
202cb93a386Sopenharmony_ciif __name__ == '__main__':
203cb93a386Sopenharmony_ci  main()
204