Initial Variation support
See: T42930 Also added tests
This commit is contained in:
@@ -119,6 +119,9 @@ def pack(
|
|||||||
# converting: '../../bar' --> '__/__/bar'
|
# converting: '../../bar' --> '__/__/bar'
|
||||||
# so all paths are nested and not moved outside the session path.
|
# so all paths are nested and not moved outside the session path.
|
||||||
blendfile_src_dir_fakeroot=None,
|
blendfile_src_dir_fakeroot=None,
|
||||||
|
|
||||||
|
# Read variations from json files.
|
||||||
|
use_variations=True,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
:param deps_remap: Store path deps_remap info as follows.
|
:param deps_remap: Store path deps_remap info as follows.
|
||||||
@@ -181,6 +184,12 @@ def pack(
|
|||||||
"""
|
"""
|
||||||
filepath = blendfile_path_walker.utils.compatpath(filepath)
|
filepath = blendfile_path_walker.utils.compatpath(filepath)
|
||||||
|
|
||||||
|
if use_variations:
|
||||||
|
if blendfile_levels_dict_curr:
|
||||||
|
filepath = blendfile_levels_dict_curr.get(filepath, filepath)
|
||||||
|
|
||||||
|
# ...
|
||||||
|
|
||||||
# first remap this blend file to the location it will end up (so we can get images relative to _that_)
|
# first remap this blend file to the location it will end up (so we can get images relative to _that_)
|
||||||
# TODO(cam) cache the results
|
# TODO(cam) cache the results
|
||||||
fp_basedir_conv = _relpath_remap(os.path.join(rootdir, b'dummy'), base_dir_src, base_dir_src, blendfile_src_dir_fakeroot)[0]
|
fp_basedir_conv = _relpath_remap(os.path.join(rootdir, b'dummy'), base_dir_src, base_dir_src, blendfile_src_dir_fakeroot)[0]
|
||||||
@@ -197,6 +206,70 @@ def pack(
|
|||||||
path_temp_files.add(filepath_tmp)
|
path_temp_files.add(filepath_tmp)
|
||||||
return filepath_tmp
|
return filepath_tmp
|
||||||
|
|
||||||
|
# -----------------
|
||||||
|
# Variation Support
|
||||||
|
#
|
||||||
|
# Use a json file to allow recursive-remapping of variations.
|
||||||
|
#
|
||||||
|
# file_a.blend
|
||||||
|
# file_a.json '{"variations": ["tree.blue.blend", ...]}'
|
||||||
|
# file_a.blend -> file_b.blend
|
||||||
|
# file_b.blend --> tree.blend
|
||||||
|
#
|
||||||
|
# the variation of `file_a.blend` causes `file_b.blend`
|
||||||
|
# to link in `tree.blue.blend`
|
||||||
|
|
||||||
|
if use_variations:
|
||||||
|
blendfile_levels = []
|
||||||
|
blendfile_levels_dict = []
|
||||||
|
blendfile_levels_dict_curr = {}
|
||||||
|
|
||||||
|
def blendfile_levels_rebuild():
|
||||||
|
# after changing blend file configurations,
|
||||||
|
# re-create current variation lookup table
|
||||||
|
blendfile_levels_dict_curr.clear()
|
||||||
|
for d in blendfile_levels_dict:
|
||||||
|
if d is not None:
|
||||||
|
blendfile_levels_dict_curr.update(d)
|
||||||
|
|
||||||
|
# use variations!
|
||||||
|
def blendfile_level_cb_enter(filepath):
|
||||||
|
import json
|
||||||
|
|
||||||
|
filepath_json = os.path.splitext(filepath)[0] + b".json"
|
||||||
|
if os.path.exists(filepath_json):
|
||||||
|
with open(filepath_json, encoding='utf-8') as f_handle:
|
||||||
|
variations = [f.encode("utf-8") for f in json.load(f_handle).get("variations")]
|
||||||
|
# convert to absolute paths
|
||||||
|
basepath = os.path.dirname(filepath)
|
||||||
|
variations = {
|
||||||
|
# Reverse lookup, from non-variation to variation we specify in this file.
|
||||||
|
# {"/abs/path/foo.png": "/abs/path/foo.variation.png", ...}
|
||||||
|
# .. where the input _is_ the variation,
|
||||||
|
# we just make it absolute and use the non-variation as
|
||||||
|
# the key to the variation value.
|
||||||
|
b".".join(f.rsplit(b".", 2)[0::2]): f for f_ in variations
|
||||||
|
for f in (os.path.normpath(os.path.join(basepath, f_)),)
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
variations = None
|
||||||
|
|
||||||
|
blendfile_levels.append(filepath)
|
||||||
|
blendfile_levels_dict.append(variations)
|
||||||
|
|
||||||
|
if variations:
|
||||||
|
blendfile_levels_rebuild()
|
||||||
|
|
||||||
|
def blendfile_level_cb_exit(filepath):
|
||||||
|
blendfile_levels.pop()
|
||||||
|
blendfile_levels_dict.pop()
|
||||||
|
|
||||||
|
if blendfile_levels_dict_curr:
|
||||||
|
blendfile_levels_rebuild()
|
||||||
|
else:
|
||||||
|
blendfile_level_cb_enter = blendfile_level_cb_exit = None
|
||||||
|
blendfile_levels_dict_curr = None
|
||||||
|
|
||||||
lib_visit = {}
|
lib_visit = {}
|
||||||
fp_blend_basename_last = b''
|
fp_blend_basename_last = b''
|
||||||
|
|
||||||
@@ -207,6 +280,10 @@ def pack(
|
|||||||
recursive=True,
|
recursive=True,
|
||||||
recursive_all=all_deps,
|
recursive_all=all_deps,
|
||||||
lib_visit=lib_visit,
|
lib_visit=lib_visit,
|
||||||
|
blendfile_level_cb=(
|
||||||
|
blendfile_level_cb_enter,
|
||||||
|
blendfile_level_cb_exit,
|
||||||
|
)
|
||||||
):
|
):
|
||||||
|
|
||||||
# we could pass this in!
|
# we could pass this in!
|
||||||
@@ -222,6 +299,15 @@ def pack(
|
|||||||
path_src = blendfile_path_walker.utils.abspath(path_rel, fp.basedir)
|
path_src = blendfile_path_walker.utils.abspath(path_rel, fp.basedir)
|
||||||
path_src = os.path.normpath(path_src)
|
path_src = os.path.normpath(path_src)
|
||||||
|
|
||||||
|
# apply variation (if available)
|
||||||
|
if use_variations:
|
||||||
|
if blendfile_levels_dict_curr:
|
||||||
|
path_src_variation = blendfile_levels_dict_curr.get(path_src)
|
||||||
|
if path_src_variation is not None:
|
||||||
|
path_src = path_src_variation
|
||||||
|
path_rel = os.path.join(os.path.dirname(path_rel), os.path.basename(path_src))
|
||||||
|
del path_src_variation
|
||||||
|
|
||||||
# destination path realtive to the root
|
# destination path realtive to the root
|
||||||
# assert(b'..' not in path_src)
|
# assert(b'..' not in path_src)
|
||||||
assert(b'..' not in base_dir_src)
|
assert(b'..' not in base_dir_src)
|
||||||
@@ -238,7 +324,6 @@ def pack(
|
|||||||
|
|
||||||
path_dst_final = b'//' + path_dst_final
|
path_dst_final = b'//' + path_dst_final
|
||||||
fp.filepath = path_dst_final
|
fp.filepath = path_dst_final
|
||||||
|
|
||||||
# add to copy-list
|
# add to copy-list
|
||||||
# never copy libs (handled separately)
|
# never copy libs (handled separately)
|
||||||
if not isinstance(fp, blendfile_path_walker.FPElem_block_path) or fp.userdata[0].code != b'LI':
|
if not isinstance(fp, blendfile_path_walker.FPElem_block_path) or fp.userdata[0].code != b'LI':
|
||||||
|
@@ -19,7 +19,8 @@
|
|||||||
# ***** END GPL LICENCE BLOCK *****
|
# ***** END GPL LICENCE BLOCK *****
|
||||||
|
|
||||||
import os
|
import os
|
||||||
VERBOSE = os.environ.get('BAM_VERBOSE', False)
|
# gives problems with scripts that use stdout, for testing 'bam deps' for eg.
|
||||||
|
# VERBOSE = os.environ.get('BAM_VERBOSE', False)
|
||||||
TIMEIT = False
|
TIMEIT = False
|
||||||
|
|
||||||
|
|
||||||
@@ -199,10 +200,17 @@ class FilePath:
|
|||||||
# prevents cyclic references too!
|
# prevents cyclic references too!
|
||||||
# {lib_path: set([block id's ...])}
|
# {lib_path: set([block id's ...])}
|
||||||
lib_visit=None,
|
lib_visit=None,
|
||||||
|
|
||||||
|
# optional blendfile callbacks
|
||||||
|
# These callbacks run on enter-exit blend files
|
||||||
|
# so you can keep track of what file and level you're at.
|
||||||
|
blendfile_level_cb=(None, None),
|
||||||
):
|
):
|
||||||
# print(level, block_codes)
|
# print(level, block_codes)
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
filepath = os.path.abspath(filepath)
|
||||||
|
|
||||||
if VERBOSE:
|
if VERBOSE:
|
||||||
indent_str = " " * level
|
indent_str = " " * level
|
||||||
# print(indent_str + "Opening:", filepath)
|
# print(indent_str + "Opening:", filepath)
|
||||||
@@ -212,7 +220,12 @@ class FilePath:
|
|||||||
log_deps.info("%s%s" % (indent_str, filepath.decode('utf-8')))
|
log_deps.info("%s%s" % (indent_str, filepath.decode('utf-8')))
|
||||||
log_deps.info("%s%s" % (indent_str, set_as_str(block_codes)))
|
log_deps.info("%s%s" % (indent_str, set_as_str(block_codes)))
|
||||||
|
|
||||||
basedir = os.path.dirname(os.path.abspath(filepath))
|
blendfile_level_cb_enter, blendfile_level_cb_exit = blendfile_level_cb
|
||||||
|
|
||||||
|
if blendfile_level_cb_enter is not None:
|
||||||
|
blendfile_level_cb_enter(filepath)
|
||||||
|
|
||||||
|
basedir = os.path.dirname(filepath)
|
||||||
if rootdir is None:
|
if rootdir is None:
|
||||||
rootdir = basedir
|
rootdir = basedir
|
||||||
|
|
||||||
@@ -416,8 +429,13 @@ class FilePath:
|
|||||||
rootdir=rootdir,
|
rootdir=rootdir,
|
||||||
level=level + 1,
|
level=level + 1,
|
||||||
lib_visit=lib_visit,
|
lib_visit=lib_visit,
|
||||||
|
blendfile_level_cb=blendfile_level_cb,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if blendfile_level_cb_exit is not None:
|
||||||
|
blendfile_level_cb_exit(filepath)
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------
|
# ------------------------------------------------------------------------
|
||||||
# Direct filepaths from Blocks
|
# Direct filepaths from Blocks
|
||||||
#
|
#
|
||||||
|
@@ -818,7 +818,7 @@ class bam_commands:
|
|||||||
print("]")
|
print("]")
|
||||||
else:
|
else:
|
||||||
for f_src, f_dst, f_dst_abs, f_status in status_walker():
|
for f_src, f_dst, f_dst_abs, f_status in status_walker():
|
||||||
print(" %r -> (%r = %r) %s" % (f_src, f_dst, f_dst_abs, f_status))ia
|
print(" %r -> (%r = %r) %s" % (f_src, f_dst, f_dst_abs, f_status))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def pack(
|
def pack(
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -860,6 +860,66 @@ class BamCheckoutTest(BamSessionTestCase):
|
|||||||
# checkout inside of the existing session, should raise exception
|
# checkout inside of the existing session, should raise exception
|
||||||
self.assertRaises(RuntimeError, bam_run, ["checkout", file_name, "--output", session_path], session_path)
|
self.assertRaises(RuntimeError, bam_run, ["checkout", file_name, "--output", session_path], session_path)
|
||||||
|
|
||||||
|
def test_checkout_variation(self):
|
||||||
|
session_name = "mysession"
|
||||||
|
proj_path, session_path = self.init_session(session_name)
|
||||||
|
variation_path = os.path.join(session_path, "variations")
|
||||||
|
|
||||||
|
if 1:
|
||||||
|
import shutil
|
||||||
|
# path cant already exist, ugh
|
||||||
|
shutil.copytree(
|
||||||
|
os.path.join(CURRENT_DIR, "blends", "variations"),
|
||||||
|
variation_path,
|
||||||
|
)
|
||||||
|
stdout, stderr = bam_run(["commit", "-m", "test message"], session_path)
|
||||||
|
self.assertEqual("", stderr)
|
||||||
|
|
||||||
|
|
||||||
|
listing = bam_run_as_json(["ls", "variations", "--json"], session_path)
|
||||||
|
listing_expect = [
|
||||||
|
["cone.blend", "file"],
|
||||||
|
["cone.blue.blend", "file"],
|
||||||
|
["lib_endpoint.blend", "file"],
|
||||||
|
["lib_user.blend", "file"],
|
||||||
|
]
|
||||||
|
|
||||||
|
self.assertEqual(listing, listing_expect)
|
||||||
|
|
||||||
|
f_variation = os.path.join(variation_path, "lib_user.json")
|
||||||
|
|
||||||
|
# now create variation file & commit it
|
||||||
|
file_quick_write(
|
||||||
|
f_variation,
|
||||||
|
data='{"variations": ["cone.blue.blend"]}',
|
||||||
|
)
|
||||||
|
|
||||||
|
stdout, stderr = bam_run(["commit", "-m", "add variation"], session_path)
|
||||||
|
self.assertEqual("", stderr)
|
||||||
|
|
||||||
|
listing = bam_run_as_json(["ls", "variations", "--json"], session_path)
|
||||||
|
listing_expect.append(["lib_user.json", "file"])
|
||||||
|
listing_expect.sort()
|
||||||
|
self.assertEqual(listing, listing_expect)
|
||||||
|
|
||||||
|
# now clear the repo and do a fresh checkout, and see that the variation is applied.
|
||||||
|
# remove the path
|
||||||
|
shutil.rmtree(session_path)
|
||||||
|
|
||||||
|
# checkout the file again
|
||||||
|
file_name = "variations/lib_endpoint.blend"
|
||||||
|
session_path = session_path + "_a"
|
||||||
|
stdout, stderr = bam_run(["checkout", file_name, "--output", session_path], proj_path)
|
||||||
|
self.assertEqual("", stderr)
|
||||||
|
|
||||||
|
ret = bam_run_as_json(["deps", "lib_endpoint.blend", "--json", "--recursive"], session_path)
|
||||||
|
ret.sort()
|
||||||
|
|
||||||
|
self.assertEqual(ret[0][1], "//lib_user.blend")
|
||||||
|
self.assertEqual(ret[0][3], "OK")
|
||||||
|
self.assertEqual(ret[1][1], "//cone.blue.blend")
|
||||||
|
self.assertEqual(ret[1][3], "OK")
|
||||||
|
|
||||||
|
|
||||||
class BamUpdateTest(BamSessionTestCase):
|
class BamUpdateTest(BamSessionTestCase):
|
||||||
"""Test for the `bam update` command.
|
"""Test for the `bam update` command.
|
||||||
@@ -1356,54 +1416,6 @@ class BamRelativeAbsoluteTest(BamSessionTestCase):
|
|||||||
# self.assertEqual(ret[0], ["house_rel.blend", "file"])
|
# self.assertEqual(ret[0], ["house_rel.blend", "file"])
|
||||||
|
|
||||||
|
|
||||||
class BamVariation(BamSessionTestCase):
|
|
||||||
"""
|
|
||||||
"""
|
|
||||||
def __init__(self, *args):
|
|
||||||
self.init_defaults()
|
|
||||||
super().__init__(*args)
|
|
||||||
|
|
||||||
def test_variation(self):
|
|
||||||
"""
|
|
||||||
"""
|
|
||||||
|
|
||||||
session_name = "mysession"
|
|
||||||
proj_path, session_path = self.init_session(session_name)
|
|
||||||
variation_path = os.path.join(session_path, "variations")
|
|
||||||
|
|
||||||
if 1:
|
|
||||||
import shutil
|
|
||||||
# path cant already exist, ugh
|
|
||||||
shutil.copytree(
|
|
||||||
os.path.join(CURRENT_DIR, "blends", "variations"),
|
|
||||||
variation_path,
|
|
||||||
)
|
|
||||||
stdout, stderr = bam_run(["commit", "-m", "test message"], session_path)
|
|
||||||
self.assertEqual("", stderr)
|
|
||||||
|
|
||||||
|
|
||||||
listing = bam_run_as_json(["ls", "variations", "--json"], session_path)
|
|
||||||
self.assertEqual(
|
|
||||||
listing,
|
|
||||||
[["cone.blue.blend", "file"],
|
|
||||||
["cone.red.blend", "file"],
|
|
||||||
["lib_endpoint.blend", "file"]],
|
|
||||||
["lib_user.blend", "file"]],
|
|
||||||
)
|
|
||||||
|
|
||||||
f_variation = os.path.join(variation_path, "lib_user.json")
|
|
||||||
|
|
||||||
# now create variation file & commit it
|
|
||||||
file_quick_write(
|
|
||||||
session_path,
|
|
||||||
f,
|
|
||||||
"",
|
|
||||||
append=True)
|
|
||||||
|
|
||||||
|
|
||||||
# import time; time.sleep(10000000)
|
|
||||||
|
|
||||||
|
|
||||||
class BamIgnoreTest(BamSessionTestCase):
|
class BamIgnoreTest(BamSessionTestCase):
|
||||||
"""Checks out a project, creates a .bamignore file with a few rules
|
"""Checks out a project, creates a .bamignore file with a few rules
|
||||||
and tries to commit files that violate them.
|
and tries to commit files that violate them.
|
||||||
|
Reference in New Issue
Block a user