diff options
Diffstat (limited to 'pgo_tools/merge_profdata_and_upload.py')
-rwxr-xr-x | pgo_tools/merge_profdata_and_upload.py | 621 |
1 files changed, 333 insertions, 288 deletions
diff --git a/pgo_tools/merge_profdata_and_upload.py b/pgo_tools/merge_profdata_and_upload.py index 15445c83..bb53ed6c 100755 --- a/pgo_tools/merge_profdata_and_upload.py +++ b/pgo_tools/merge_profdata_and_upload.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright 2019 The Chromium OS Authors. All rights reserved. +# Copyright 2019 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -35,7 +35,6 @@ In this example, the script will merge profdata from arm and amd64 builder, and profdata from an arm64 buildbucket task. """ -from __future__ import print_function import argparse import collections @@ -48,328 +47,374 @@ import subprocess import sys import tempfile -_LLVM_PROFDATA = '/usr/bin/llvm-profdata' -_GS_PREFIX = 'gs://' -_LLVMMetadata = collections.namedtuple('_LLVMMetadata', ['head_sha']) +_LLVM_PROFDATA = "/usr/bin/llvm-profdata" +_GS_PREFIX = "gs://" + +_LLVMMetadata = collections.namedtuple("_LLVMMetadata", ["head_sha"]) def _fetch_gs_artifact(remote_name, local_name): - """Fetch single file from remote gs location to local. + """Fetch single file from remote gs location to local. - Args: - remote_name: full gs location to the file. - local_name: the name of local file to be copied to. - """ - assert remote_name.startswith(_GS_PREFIX) - subprocess.check_call(['gsutil', 'cp', remote_name, local_name]) + Args: + remote_name: full gs location to the file. + local_name: the name of local file to be copied to. + """ + assert remote_name.startswith(_GS_PREFIX) + subprocess.check_call(["gsutil", "cp", remote_name, local_name]) def _get_gs_profdata(remote_profdata, arch): - """Fetch and extract profdata from remote gs location. + """Fetch and extract profdata from remote gs location. - Args: - remote_profdata: remote gs location of the profdata tarball. - arch: directory named with arch to saperate each profdata. + Args: + remote_profdata: remote gs location of the profdata tarball. + arch: directory named with arch to saperate each profdata. - Returns: - Local location of the extracted profdata. - """ - tar = 'llvm_profdata.tar.xz' - _fetch_gs_artifact(remote_profdata, tar) - extract_cmd = ['tar', '-xvf', tar] + Returns: + Local location of the extracted profdata. + """ + tar = "llvm_profdata.tar.xz" + _fetch_gs_artifact(remote_profdata, tar) + extract_cmd = ["tar", "-xvf", tar] - profdata_name = subprocess.check_output(extract_cmd).strip() - # The output of the `tar` command should only contain one line of the - # extracted profdata name. - if b'.llvm.profdata' not in profdata_name: - raise RuntimeError('No profdata in the tarball: %s' % remote_profdata) + profdata_name = subprocess.check_output(extract_cmd).strip() + # The output of the `tar` command should only contain one line of the + # extracted profdata name. + if b".llvm.profdata" not in profdata_name: + raise RuntimeError("No profdata in the tarball: %s" % remote_profdata) - os.mkdir(arch) - profdata_loc = os.path.join(arch, 'llvm.profdata') - os.rename(profdata_name, profdata_loc) - print('Profdata extracted to: %s' % profdata_loc) - return profdata_loc + os.mkdir(arch) + profdata_loc = os.path.join(arch, "llvm.profdata") + os.rename(profdata_name, profdata_loc) + print("Profdata extracted to: %s" % profdata_loc) + return profdata_loc def _get_gs_metadata(remote_metadata): - """Fetch metadata from remote gs location and read the LLVM head_sha. + """Fetch metadata from remote gs location and read the LLVM head_sha. - Args: - remote_metadata: remote gs location of the metadata json file. + Args: + remote_metadata: remote gs location of the metadata json file. - Returns: - LLVM head_sha metadata - """ - metadata_basename = 'llvm_metadata.json' - _fetch_gs_artifact(remote_metadata, metadata_basename) + Returns: + LLVM head_sha metadata + """ + metadata_basename = "llvm_metadata.json" + _fetch_gs_artifact(remote_metadata, metadata_basename) - with open(metadata_basename) as f: - result = json.load(f) + with open(metadata_basename) as f: + result = json.load(f) - return _LLVMMetadata(head_sha=result['head_sha']) + return _LLVMMetadata(head_sha=result["head_sha"]) def _find_latest_artifacts(gs_url, arch): - """Fetch the latest profdata and metadata from a give gs location. - - Args: - gs_url: a gs location containing one or more artifacts to fetch. - arch: the arch profdata collected from. - - Returns: - A tuple of local profdata location and metadata - """ - assert gs_url.startswith(_GS_PREFIX) - try: - # List all artifacts in the gs location and sort by time. - output = subprocess.check_output(['gsutil', 'ls', '-l', gs_url], - encoding='utf-8').strip().split('\n') - lines = sorted(output, key=lambda x: x.split()[1], reverse=True) - except subprocess.CalledProcessError: - raise RuntimeError('Artifacts not found: %s' % gs_url) - - # Use a loop to go through all artifacts to find the latest profdata. - # An example of the output of latest builder bucket: - # pylint: disable=line-too-long - # 5006528 2020-05-31T10:08:48Z gs://chromeos-toolchain-artifacts/llvm-pgo/arm/llvm-11.0_pre387436_p20200403-r7-a8e5dcb072b1f794883ae8125fb08c06db678d56.llvm.profdata.tar.xz - # 56 2020-05-31T10:08:48Z gs://chromeos-toolchain-artifacts/llvm-pgo/arm/llvm-11.0_pre387436_p20200403-r7-a8e5dcb072b1f794883ae8125fb08c06db678d56.llvm_metadata.json - # 5005952 2020-05-24T10:53:34Z gs://chromeos-toolchain-artifacts/llvm-pgo/arm/llvm-11.0_pre387436_p20200403-r5-a8e5dcb072b1f794883ae8125fb08c06db678d56.llvm.profdata.tar.xz - # 56 2020-05-24T10:53:34Z gs://chromeos-toolchain-artifacts/llvm-pgo/arm/llvm-11.0_pre387436_p20200403-r5-a8e5dcb072b1f794883ae8125fb08c06db678d56.llvm_metadata.json - # An example for the lines of buildbucket location: - # 5004260 2020-05-29T09:48:04Z gs://chromeos-image-archive/arm-pgo-generate-llvm-next-toolchain/R85-13254.0.0-1-8879010326583123168/llvm-11.0_pre387436_p20200403-r7-a8e5dcb072b1f794883ae8125fb08c06db678d56.llvm.profdata.tar.xz - # 56 2020-05-29T09:48:04Z gs://chromeos-image-archive/arm-pgo-generate-llvm-next-toolchain/R85-13254.0.0-1-8879010326583123168/llvm-11.0_pre387436_p20200403-r7-a8e5dcb072b1f794883ae8125fb08c06db678d56.llvm_metadata.json - # pylint: enable=line-too-long - profdata_url = '' - for line in lines: - url = line.split()[-1] - if '.llvm.profdata.tar.xz' in url: - profile_path = _get_gs_profdata(url, arch) - profdata_url = url - break - if not profile_path or not profdata_url: - raise RuntimeError('No profdata found from %s' % gs_url) - - metadata_url = profdata_url.replace('.llvm.profdata.tar.xz', - '.llvm_metadata.json') - metadata = _get_gs_metadata(metadata_url) - if not metadata: - raise RuntimeError('No metadata found from %s' % gs_url) - return metadata, profile_path + """Fetch the latest profdata and metadata from a give gs location. + + Args: + gs_url: a gs location containing one or more artifacts to fetch. + arch: the arch profdata collected from. + + Returns: + A tuple of local profdata location and metadata + """ + assert gs_url.startswith(_GS_PREFIX) + try: + # List all artifacts in the gs location and sort by time. + output = ( + subprocess.check_output( + ["gsutil", "ls", "-l", gs_url], encoding="utf-8" + ) + .strip() + .split("\n") + ) + lines = sorted(output, key=lambda x: x.split()[1], reverse=True) + except subprocess.CalledProcessError: + raise RuntimeError("Artifacts not found: %s" % gs_url) + + # Use a loop to go through all artifacts to find the latest profdata. + # An example of the output of latest builder bucket: + # pylint: disable=line-too-long + # 5006528 2020-05-31T10:08:48Z gs://chromeos-toolchain-artifacts/llvm-pgo/arm/llvm-11.0_pre387436_p20200403-r7-a8e5dcb072b1f794883ae8125fb08c06db678d56.llvm.profdata.tar.xz + # 56 2020-05-31T10:08:48Z gs://chromeos-toolchain-artifacts/llvm-pgo/arm/llvm-11.0_pre387436_p20200403-r7-a8e5dcb072b1f794883ae8125fb08c06db678d56.llvm_metadata.json + # 5005952 2020-05-24T10:53:34Z gs://chromeos-toolchain-artifacts/llvm-pgo/arm/llvm-11.0_pre387436_p20200403-r5-a8e5dcb072b1f794883ae8125fb08c06db678d56.llvm.profdata.tar.xz + # 56 2020-05-24T10:53:34Z gs://chromeos-toolchain-artifacts/llvm-pgo/arm/llvm-11.0_pre387436_p20200403-r5-a8e5dcb072b1f794883ae8125fb08c06db678d56.llvm_metadata.json + # An example for the lines of buildbucket location: + # 5004260 2020-05-29T09:48:04Z gs://chromeos-image-archive/arm-pgo-generate-llvm-next-toolchain/R85-13254.0.0-1-8879010326583123168/llvm-11.0_pre387436_p20200403-r7-a8e5dcb072b1f794883ae8125fb08c06db678d56.llvm.profdata.tar.xz + # 56 2020-05-29T09:48:04Z gs://chromeos-image-archive/arm-pgo-generate-llvm-next-toolchain/R85-13254.0.0-1-8879010326583123168/llvm-11.0_pre387436_p20200403-r7-a8e5dcb072b1f794883ae8125fb08c06db678d56.llvm_metadata.json + # pylint: enable=line-too-long + profdata_url = "" + for line in lines: + url = line.split()[-1] + if ".llvm.profdata.tar.xz" in url: + profile_path = _get_gs_profdata(url, arch) + profdata_url = url + break + if not profile_path or not profdata_url: + raise RuntimeError("No profdata found from %s" % gs_url) + + metadata_url = profdata_url.replace( + ".llvm.profdata.tar.xz", ".llvm_metadata.json" + ) + metadata = _get_gs_metadata(metadata_url) + if not metadata: + raise RuntimeError("No metadata found from %s" % gs_url) + return metadata, profile_path def _fetch_from_latest(arch): - """Fetch artifacts from latest builders. + """Fetch artifacts from latest builders. - Args: - arch: the arch profdata collected from. + Args: + arch: the arch profdata collected from. - Returns: - A tuple of local profdata location and metadata - """ - print('\nFETCHING LATEST PROFDATA ON %s...' % arch.upper()) - remote_latest = ( - '%schromeos-toolchain-artifacts/llvm-pgo/%s' % (_GS_PREFIX, arch)) - return _find_latest_artifacts(remote_latest, arch) + Returns: + A tuple of local profdata location and metadata + """ + print("\nFETCHING LATEST PROFDATA ON %s..." % arch.upper()) + remote_latest = "%schromeos-toolchain-artifacts/llvm-pgo/%s" % ( + _GS_PREFIX, + arch, + ) + return _find_latest_artifacts(remote_latest, arch) def _fetch_from_buildbucket(arch, bb): - """Fetch artifacts from buildbucket task. - - Args: - arch: the arch profdata collected from. - bb: buildbucket id. - - Returns: - A tuple of local profdata location and metadata - """ - print('\nFETCHING BUILDBUCKET PROFDATA ON %s...' % arch.upper()) - remote_arch = ('%schromeos-image-archive/%s-pgo-generate-llvm-next-toolchain' - % (_GS_PREFIX, arch)) - # List all buckets under {arch}-pgo-generate-llvm-next-toolchain and - # grep with buildbucket id. - remote_bb = subprocess.check_output(['gsutil', 'ls', remote_arch], - encoding='utf-8').strip().split('\n') - for line in remote_bb: - if bb in line: - return _find_latest_artifacts(line, arch) - raise RuntimeError('No matched results found in %s with bb: %s' % (arch, bb)) + """Fetch artifacts from buildbucket task. + + Args: + arch: the arch profdata collected from. + bb: buildbucket id. + + Returns: + A tuple of local profdata location and metadata + """ + print("\nFETCHING BUILDBUCKET PROFDATA ON %s..." % arch.upper()) + remote_arch = ( + "%schromeos-image-archive/%s-pgo-generate-llvm-next-toolchain" + % ( + _GS_PREFIX, + arch, + ) + ) + # List all buckets under {arch}-pgo-generate-llvm-next-toolchain and + # grep with buildbucket id. + remote_bb = ( + subprocess.check_output(["gsutil", "ls", remote_arch], encoding="utf-8") + .strip() + .split("\n") + ) + for line in remote_bb: + if bb in line: + return _find_latest_artifacts(line, arch) + raise RuntimeError( + "No matched results found in %s with bb: %s" % (arch, bb) + ) def _merge_profdata(profdata_list, output_name): - """Merge profdata. - - Args: - profdata_list: list of profdata location of each arch. - output_name: name of merged profdata. - """ - merge_cmd = [_LLVM_PROFDATA, 'merge', '-output', output_name] + profdata_list - print('\nMerging PGO profiles.\nCMD: %s' % merge_cmd) - subprocess.check_call(merge_cmd) + """Merge profdata. + + Args: + profdata_list: list of profdata location of each arch. + output_name: name of merged profdata. + """ + merge_cmd = [ + _LLVM_PROFDATA, + "merge", + "-output", + output_name, + ] + profdata_list + print("\nMerging PGO profiles.\nCMD: %s" % merge_cmd) + subprocess.check_call(merge_cmd) def _tar_and_upload_profdata(profdata, name_suffix): - """Create a tarball of merged profdata and upload to certain gs location. - - Args: - profdata: location of merged profdata. - name_suffix: usually the LLVM head_sha. - """ - tarball = 'llvm-profdata-%s.tar.xz' % name_suffix - print('Making profdata tarball: %s' % tarball) - subprocess.check_call( - ['tar', '--sparse', '-I', 'xz', '-cf', tarball, profdata]) - - upload_location = '%schromeos-localmirror/distfiles/%s' % (_GS_PREFIX, - tarball) - - # TODO: it's better to create a subdir: distfiles/llvm_pgo_profile, but - # now llvm could only recognize distfiles. - upload_cmd = [ - 'gsutil', - '-m', - 'cp', - '-n', - '-a', - 'public-read', - tarball, - upload_location, - ] - print('\nUploading tarball to gs.\nCMD: %s\n' % upload_cmd) - - # gsutil prints all status to stderr, oddly enough. - gs_output = subprocess.check_output( - upload_cmd, stderr=subprocess.STDOUT, encoding='utf-8') - - # gsutil exits successfully even if it uploaded nothing. It prints a summary - # of what all it did, though. Successful uploads are just a progress bar, - # unsuccessful ones note that items were skipped. - if 'Skipping existing item' in gs_output: - raise ValueError('Profile upload failed: would overwrite an existing ' - 'profile at %s' % upload_location) + """Create a tarball of merged profdata and upload to certain gs location. + + Args: + profdata: location of merged profdata. + name_suffix: usually the LLVM head_sha. + """ + tarball = "llvm-profdata-%s.tar.xz" % name_suffix + print("Making profdata tarball: %s" % tarball) + subprocess.check_call( + ["tar", "--sparse", "-I", "xz", "-cf", tarball, profdata] + ) + + upload_location = "%schromeos-localmirror/distfiles/%s" % ( + _GS_PREFIX, + tarball, + ) + + # TODO: it's better to create a subdir: distfiles/llvm_pgo_profile, but + # now llvm could only recognize distfiles. + upload_cmd = [ + "gsutil", + "-m", + "cp", + "-n", + "-a", + "public-read", + tarball, + upload_location, + ] + print("\nUploading tarball to gs.\nCMD: %s\n" % upload_cmd) + + # gsutil prints all status to stderr, oddly enough. + gs_output = subprocess.check_output( + upload_cmd, stderr=subprocess.STDOUT, encoding="utf-8" + ) + + # gsutil exits successfully even if it uploaded nothing. It prints a summary + # of what all it did, though. Successful uploads are just a progress bar, + # unsuccessful ones note that items were skipped. + if "Skipping existing item" in gs_output: + raise ValueError( + "Profile upload failed: would overwrite an existing " + "profile at %s" % upload_location + ) def main(): - parser = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) - parser.add_argument( - '-a', - '--all_latest_profiles', - action='store_true', - help='Merge and upload profiles from the latest builders.') - parser.add_argument( - '-l', - '--latest', - default=[], - action='append', - help='User can specify the profdata from which builder with specific ' - 'architecture to download. By default, we merge profdata from arm, ' - 'arm64, amd64.') - parser.add_argument( - '-b', - '--buildbucket', - default=[], - action='append', - help='Extra pgo-generate-llvm-next-toolchain buildbucket results to be ' - 'used. Format should be: {arch}/{bb_id}.') - parser.add_argument( - '-o', - '--output', - default='llvm.profdata', - help='Where to put merged PGO profile. The default is to not save it ' - 'anywhere.') - parser.add_argument( - '--llvm_hash', - help='The LLVM hash to select for the profiles. Generally autodetected.') - args = parser.parse_args() - - if not args.all_latest_profiles and not (args.latest or args.buildbucket): - parser.error('Please specify whether to use latest profiles or ' - 'profiles from buildbucket') - - if args.all_latest_profiles and (args.latest or args.buildbucket): - parser.error('--all_latest_profiles cannot be specified together ' - 'with --latest or --buildbucket') - - latest = ['arm', 'arm64', 'amd64'] \ - if args.all_latest_profiles else args.latest - - all_arch_list = latest.copy() - arch_bb_list = [] - if args.buildbucket: - for arch_bb in args.buildbucket: - arch, bb = arch_bb.split('/') - arch_bb_list.append((arch, bb)) - all_arch_list.append(arch) - - if len(set(all_arch_list)) != len(all_arch_list): - parser.error('Each arch can be only passed once.') - - if not distutils.spawn.find_executable(_LLVM_PROFDATA): - sys.exit(_LLVM_PROFDATA + ' not found; are you in the chroot?') - - initial_dir = os.getcwd() - temp_dir = tempfile.mkdtemp(prefix='merge_pgo') - success = True - try: - os.chdir(temp_dir) - profdata_list = [] - heads = set() - - def append_artifacts(fetched_tuple): - llvm_metadata, profdata_loc = fetched_tuple - if os.path.getsize(profdata_loc) < 512 * 1024: - raise RuntimeError('The PGO profile in local path %s is suspiciously ' - 'small. Something might have gone ' - 'wrong.' % profdata_loc) - heads.add(llvm_metadata.head_sha) - profdata_list.append(profdata_loc) - - for arch in latest: - append_artifacts(_fetch_from_latest(arch)) - - for arch, bb in arch_bb_list: - append_artifacts(_fetch_from_buildbucket(arch, bb)) - - assert heads, "Didn't fetch anything?" - - def die_with_head_complaint(complaint): - extra = ' (HEADs found: %s)' % sorted(heads) - raise RuntimeError(complaint.rstrip() + extra) - - llvm_hash = args.llvm_hash - if not llvm_hash: - if len(heads) != 1: - die_with_head_complaint( - '%d LLVM HEADs were found, which is more than one. You probably ' - 'want a consistent set of HEADs for a profile. If you know you ' - "don't, please specify --llvm_hash, and note that *all* profiles " - 'will be merged into this final profile, regardless of their ' - 'reported HEAD.' % len(heads)) - llvm_hash, = heads - - if llvm_hash not in heads: - assert llvm_hash == args.llvm_hash - die_with_head_complaint( - "HEAD %s wasn't found in any fetched artifacts." % llvm_hash) - - print('\nUsing LLVM hash: %s' % llvm_hash) - - _merge_profdata(profdata_list, args.output) - print('Merged profdata locates at %s' % os.path.abspath(args.output)) - _tar_and_upload_profdata(args.output, name_suffix=llvm_hash) - print('\nMerged profdata uploaded successfully.') - except: - success = False - raise - finally: - os.chdir(initial_dir) - if success: - print('Clearing temp directory.') - shutil.rmtree(temp_dir, ignore_errors=True) - else: - print('Script fails, temp directory is at: %s' % temp_dir) - - -if __name__ == '__main__': - sys.exit(main()) + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument( + "-a", + "--all_latest_profiles", + action="store_true", + help="Merge and upload profiles from the latest builders.", + ) + parser.add_argument( + "-l", + "--latest", + default=[], + action="append", + help="User can specify the profdata from which builder with specific " + "architecture to download. By default, we merge profdata from arm, " + "arm64, amd64.", + ) + parser.add_argument( + "-b", + "--buildbucket", + default=[], + action="append", + help="Extra pgo-generate-llvm-next-toolchain buildbucket results to be " + "used. Format should be: {arch}/{bb_id}.", + ) + parser.add_argument( + "-o", + "--output", + default="llvm.profdata", + help="Where to put merged PGO profile. The default is to not save it " + "anywhere.", + ) + parser.add_argument( + "--llvm_hash", + help="The LLVM hash to select for the profiles. Generally autodetected.", + ) + args = parser.parse_args() + + if not args.all_latest_profiles and not (args.latest or args.buildbucket): + parser.error( + "Please specify whether to use latest profiles or " + "profiles from buildbucket" + ) + + if args.all_latest_profiles and (args.latest or args.buildbucket): + parser.error( + "--all_latest_profiles cannot be specified together " + "with --latest or --buildbucket" + ) + + latest = ( + ["arm", "arm64", "amd64"] if args.all_latest_profiles else args.latest + ) + + all_arch_list = latest.copy() + arch_bb_list = [] + if args.buildbucket: + for arch_bb in args.buildbucket: + arch, bb = arch_bb.split("/") + arch_bb_list.append((arch, bb)) + all_arch_list.append(arch) + + if len(set(all_arch_list)) != len(all_arch_list): + parser.error("Each arch can be only passed once.") + + if not distutils.spawn.find_executable(_LLVM_PROFDATA): + sys.exit(_LLVM_PROFDATA + " not found; are you in the chroot?") + + initial_dir = os.getcwd() + temp_dir = tempfile.mkdtemp(prefix="merge_pgo") + success = True + try: + os.chdir(temp_dir) + profdata_list = [] + heads = set() + + def append_artifacts(fetched_tuple): + llvm_metadata, profdata_loc = fetched_tuple + if os.path.getsize(profdata_loc) < 512 * 1024: + raise RuntimeError( + "The PGO profile in local path %s is suspiciously " + "small. Something might have gone " + "wrong." % profdata_loc + ) + heads.add(llvm_metadata.head_sha) + profdata_list.append(profdata_loc) + + for arch in latest: + append_artifacts(_fetch_from_latest(arch)) + + for arch, bb in arch_bb_list: + append_artifacts(_fetch_from_buildbucket(arch, bb)) + + assert heads, "Didn't fetch anything?" + + def die_with_head_complaint(complaint): + extra = " (HEADs found: %s)" % sorted(heads) + raise RuntimeError(complaint.rstrip() + extra) + + llvm_hash = args.llvm_hash + if not llvm_hash: + if len(heads) != 1: + die_with_head_complaint( + "%d LLVM HEADs were found, which is more than one. You probably " + "want a consistent set of HEADs for a profile. If you know you " + "don't, please specify --llvm_hash, and note that *all* profiles " + "will be merged into this final profile, regardless of their " + "reported HEAD." % len(heads) + ) + (llvm_hash,) = heads + + if llvm_hash not in heads: + assert llvm_hash == args.llvm_hash + die_with_head_complaint( + "HEAD %s wasn't found in any fetched artifacts." % llvm_hash + ) + + print("\nUsing LLVM hash: %s" % llvm_hash) + + _merge_profdata(profdata_list, args.output) + print("Merged profdata locates at %s" % os.path.abspath(args.output)) + _tar_and_upload_profdata(args.output, name_suffix=llvm_hash) + print("\nMerged profdata uploaded successfully.") + except: + success = False + raise + finally: + os.chdir(initial_dir) + if success: + print("Clearing temp directory.") + shutil.rmtree(temp_dir, ignore_errors=True) + else: + print("Script fails, temp directory is at: %s" % temp_dir) + + +if __name__ == "__main__": + sys.exit(main()) |