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