diff --git a/blender_asset_tracer/cli/common.py b/blender_asset_tracer/cli/common.py index 3f9118a..d93fd78 100644 --- a/blender_asset_tracer/cli/common.py +++ b/blender_asset_tracer/cli/common.py @@ -1,4 +1,5 @@ """Common functionality for CLI parsers.""" +import pathlib def add_flag(argparser, flag_name: str, **kwargs): @@ -14,3 +15,9 @@ def add_flag(argparser, flag_name: str, **kwargs): **kwargs) +def shorten(cwd: pathlib.Path, somepath: pathlib.Path) -> pathlib.Path: + """Return 'somepath' relative to CWD if possible.""" + try: + return somepath.relative_to(cwd) + except ValueError: + return somepath diff --git a/blender_asset_tracer/cli/list_deps.py b/blender_asset_tracer/cli/list_deps.py index c6ca5f9..49169a7 100644 --- a/blender_asset_tracer/cli/list_deps.py +++ b/blender_asset_tracer/cli/list_deps.py @@ -1,4 +1,5 @@ """List dependencies of a blend file.""" +import functools import json import logging import pathlib @@ -34,14 +35,7 @@ def cli_list(args): def report_text(bpath): reported_assets = set() last_reported_bfile = None - cwd = pathlib.Path.cwd() - - def shorten(somepath: pathlib.Path) -> pathlib.Path: - """Return 'somepath' relative to CWD if possible.""" - try: - return somepath.relative_to(cwd) - except ValueError: - return somepath + shorten = functools.partial(common.shorten, pathlib.Path.cwd()) for usage in tracer.deps(bpath): filepath = usage.block.bfile.filepath.absolute() diff --git a/blender_asset_tracer/cli/pack.py b/blender_asset_tracer/cli/pack.py index ba6b017..76a9f8b 100644 --- a/blender_asset_tracer/cli/pack.py +++ b/blender_asset_tracer/cli/pack.py @@ -1,4 +1,13 @@ """Create a BAT-pack for the given blend file.""" +import functools +import logging +import pathlib +import shutil + +from blender_asset_tracer import tracer +from . import common + +log = logging.getLogger(__name__) def add_parser(subparsers): @@ -6,7 +15,47 @@ def add_parser(subparsers): parser = subparsers.add_parser('pack', help=__doc__) parser.set_defaults(func=cli_pack) + parser.add_argument('blendfile', type=pathlib.Path) + parser.add_argument('target', type=pathlib.Path) def cli_pack(args): - raise NotImplementedError('bat pack not implemented yet') + bpath = args.blendfile + if not bpath.exists(): + log.fatal('File %s does not exist', args.bpath) + return 3 + + tpath = args.target + if tpath.exists() and not tpath.is_dir(): + log.fatal('Target %s exists and is not a directory', tpath) + return 4 + + shorten = functools.partial(common.shorten, pathlib.Path.cwd()) + already_copied = set() + for usage in tracer.deps(bpath): + if usage.asset_path.is_absolute(): + raise NotImplementedError('Sorry, cannot handle absolute paths yet: %s' % usage) + + for assetpath in usage.files(): + try: + assetpath = assetpath.resolve() + except FileNotFoundError: + log.error('Dependency %s does not exist', assetpath) + + if assetpath in already_copied: + log.debug('Already copied %s', assetpath) + continue + already_copied.add(assetpath) + + relpath = shorten(assetpath) + if relpath.is_absolute(): + raise NotImplementedError('Sorry, cannot handle absolute paths yet: %s in %s' + % (usage, assetpath)) + + full_target = tpath / relpath + full_target.parent.mkdir(parents=True, exist_ok=True) + # TODO(Sybren): when we target Py 3.6+, remove the str() calls. + print(relpath) + shutil.copyfile(str(assetpath), str(full_target)) + + log.info('Copied %d files to %s', len(already_copied), tpath) diff --git a/blender_asset_tracer/tracer/file2blocks.py b/blender_asset_tracer/tracer/file2blocks.py index e7c542b..283f720 100644 --- a/blender_asset_tracer/tracer/file2blocks.py +++ b/blender_asset_tracer/tracer/file2blocks.py @@ -35,7 +35,7 @@ class _BlockIterator: limit_to: typing.Set[blendfile.BlendFileBlock] = frozenset(), ) -> typing.Iterator[blendfile.BlendFileBlock]: """Expand blocks with dependencies from other libraries.""" - bpath = bfile.filepath + bpath = bfile.filepath.absolute().resolve() root_dir = bpathlib.BlendPath(bpath.parent) # Mapping from library path to data blocks to expand. @@ -79,6 +79,12 @@ class _BlockIterator: # and iterate over the blocks referred there. for lib_bpath, idblocks in blocks_per_lib.items(): lib_path = lib_bpath.to_path() + try: + lib_path = lib_path.resolve() + except FileNotFoundError: + log.warning('Library %s does not exist', lib_path) + continue + log.debug('Expanding %d blocks in %s', len(idblocks), lib_path) with blendfile.BlendFile(lib_path) as libfile: