1*4882a593Smuzhiyun# Development tool - export command plugin 2*4882a593Smuzhiyun# 3*4882a593Smuzhiyun# Copyright (C) 2014-2017 Intel Corporation 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 6*4882a593Smuzhiyun# 7*4882a593Smuzhiyun"""Devtool export plugin""" 8*4882a593Smuzhiyun 9*4882a593Smuzhiyunimport os 10*4882a593Smuzhiyunimport argparse 11*4882a593Smuzhiyunimport tarfile 12*4882a593Smuzhiyunimport logging 13*4882a593Smuzhiyunimport datetime 14*4882a593Smuzhiyunimport json 15*4882a593Smuzhiyun 16*4882a593Smuzhiyunlogger = logging.getLogger('devtool') 17*4882a593Smuzhiyun 18*4882a593Smuzhiyun# output files 19*4882a593Smuzhiyundefault_arcname_prefix = "workspace-export" 20*4882a593Smuzhiyunmetadata = '.export_metadata' 21*4882a593Smuzhiyun 22*4882a593Smuzhiyundef export(args, config, basepath, workspace): 23*4882a593Smuzhiyun """Entry point for the devtool 'export' subcommand""" 24*4882a593Smuzhiyun 25*4882a593Smuzhiyun def add_metadata(tar): 26*4882a593Smuzhiyun """Archive the workspace object""" 27*4882a593Smuzhiyun # finally store the workspace metadata 28*4882a593Smuzhiyun with open(metadata, 'w') as fd: 29*4882a593Smuzhiyun fd.write(json.dumps((config.workspace_path, workspace))) 30*4882a593Smuzhiyun tar.add(metadata) 31*4882a593Smuzhiyun os.unlink(metadata) 32*4882a593Smuzhiyun 33*4882a593Smuzhiyun def add_recipe(tar, recipe, data): 34*4882a593Smuzhiyun """Archive recipe with proper arcname""" 35*4882a593Smuzhiyun # Create a map of name/arcnames 36*4882a593Smuzhiyun arcnames = [] 37*4882a593Smuzhiyun for key, name in data.items(): 38*4882a593Smuzhiyun if name: 39*4882a593Smuzhiyun if key == 'srctree': 40*4882a593Smuzhiyun # all sources, no matter where are located, goes into the sources directory 41*4882a593Smuzhiyun arcname = 'sources/%s' % recipe 42*4882a593Smuzhiyun else: 43*4882a593Smuzhiyun arcname = name.replace(config.workspace_path, '') 44*4882a593Smuzhiyun arcnames.append((name, arcname)) 45*4882a593Smuzhiyun 46*4882a593Smuzhiyun for name, arcname in arcnames: 47*4882a593Smuzhiyun tar.add(name, arcname=arcname) 48*4882a593Smuzhiyun 49*4882a593Smuzhiyun 50*4882a593Smuzhiyun # Make sure workspace is non-empty and possible listed include/excluded recipes are in workspace 51*4882a593Smuzhiyun if not workspace: 52*4882a593Smuzhiyun logger.info('Workspace contains no recipes, nothing to export') 53*4882a593Smuzhiyun return 0 54*4882a593Smuzhiyun else: 55*4882a593Smuzhiyun for param, recipes in {'include':args.include,'exclude':args.exclude}.items(): 56*4882a593Smuzhiyun for recipe in recipes: 57*4882a593Smuzhiyun if recipe not in workspace: 58*4882a593Smuzhiyun logger.error('Recipe (%s) on %s argument not in the current workspace' % (recipe, param)) 59*4882a593Smuzhiyun return 1 60*4882a593Smuzhiyun 61*4882a593Smuzhiyun name = args.file 62*4882a593Smuzhiyun 63*4882a593Smuzhiyun default_name = "%s-%s.tar.gz" % (default_arcname_prefix, datetime.datetime.now().strftime('%Y%m%d%H%M%S')) 64*4882a593Smuzhiyun if not name: 65*4882a593Smuzhiyun name = default_name 66*4882a593Smuzhiyun else: 67*4882a593Smuzhiyun # if name is a directory, append the default name 68*4882a593Smuzhiyun if os.path.isdir(name): 69*4882a593Smuzhiyun name = os.path.join(name, default_name) 70*4882a593Smuzhiyun 71*4882a593Smuzhiyun if os.path.exists(name) and not args.overwrite: 72*4882a593Smuzhiyun logger.error('Tar archive %s exists. Use --overwrite/-o to overwrite it') 73*4882a593Smuzhiyun return 1 74*4882a593Smuzhiyun 75*4882a593Smuzhiyun # if all workspace is excluded, quit 76*4882a593Smuzhiyun if not len(set(workspace.keys()).difference(set(args.exclude))): 77*4882a593Smuzhiyun logger.warning('All recipes in workspace excluded, nothing to export') 78*4882a593Smuzhiyun return 0 79*4882a593Smuzhiyun 80*4882a593Smuzhiyun exported = [] 81*4882a593Smuzhiyun with tarfile.open(name, 'w:gz') as tar: 82*4882a593Smuzhiyun if args.include: 83*4882a593Smuzhiyun for recipe in args.include: 84*4882a593Smuzhiyun add_recipe(tar, recipe, workspace[recipe]) 85*4882a593Smuzhiyun exported.append(recipe) 86*4882a593Smuzhiyun else: 87*4882a593Smuzhiyun for recipe, data in workspace.items(): 88*4882a593Smuzhiyun if recipe not in args.exclude: 89*4882a593Smuzhiyun add_recipe(tar, recipe, data) 90*4882a593Smuzhiyun exported.append(recipe) 91*4882a593Smuzhiyun 92*4882a593Smuzhiyun add_metadata(tar) 93*4882a593Smuzhiyun 94*4882a593Smuzhiyun logger.info('Tar archive created at %s with the following recipes: %s' % (name, ', '.join(exported))) 95*4882a593Smuzhiyun return 0 96*4882a593Smuzhiyun 97*4882a593Smuzhiyundef register_commands(subparsers, context): 98*4882a593Smuzhiyun """Register devtool export subcommands""" 99*4882a593Smuzhiyun parser = subparsers.add_parser('export', 100*4882a593Smuzhiyun help='Export workspace into a tar archive', 101*4882a593Smuzhiyun description='Export one or more recipes from current workspace into a tar archive', 102*4882a593Smuzhiyun group='advanced') 103*4882a593Smuzhiyun 104*4882a593Smuzhiyun parser.add_argument('--file', '-f', help='Output archive file name') 105*4882a593Smuzhiyun parser.add_argument('--overwrite', '-o', action="store_true", help='Overwrite previous export tar archive') 106*4882a593Smuzhiyun group = parser.add_mutually_exclusive_group() 107*4882a593Smuzhiyun group.add_argument('--include', '-i', nargs='+', default=[], help='Include recipes into the tar archive') 108*4882a593Smuzhiyun group.add_argument('--exclude', '-e', nargs='+', default=[], help='Exclude recipes into the tar archive') 109*4882a593Smuzhiyun parser.set_defaults(func=export) 110