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