From d136ac3beae161ef1bbfaf10c7c82461c0740646 Mon Sep 17 00:00:00 2001 From: Alex Fuller Date: Wed, 8 Apr 2026 15:38:45 -0600 Subject: [PATCH] Cycles : Update to 5.1.0 with even sample motion blur patches --- Changes.md | 5 +- Cycles/config.py | 8 +- ...ype-get_type_names-for-iterating-all.patch | 180 ++++++++++++++++++ ...eometry-motion-blur-with-an-even-num.patch | 172 +++++++++++++++++ ...03-Fix-Cycles-Incorrect-BVH2-asserts.patch | 100 ++++++++++ Cycles/patches/cudaBinaries.patch | 2 +- SSE2NEON/config.py | 34 ++++ build.py | 6 + 8 files changed, 504 insertions(+), 3 deletions(-) create mode 100644 Cycles/patches/0001-Cycles-Add-NodeType-get_type_names-for-iterating-all.patch create mode 100644 Cycles/patches/0002-Cycles-Support-geometry-motion-blur-with-an-even-num.patch create mode 100644 Cycles/patches/0003-Fix-Cycles-Incorrect-BVH2-asserts.patch create mode 100644 SSE2NEON/config.py diff --git a/Changes.md b/Changes.md index 98a8bee976..a43dfd88e0 100644 --- a/Changes.md +++ b/Changes.md @@ -1,7 +1,10 @@ 11.x.x (relative to 11.0.0a4) ------ - +- Cycles : + - Updated to version 5.1.0. + - Applied patch to support geometry motion blur with an even number of steps (from Blender upstream). +- SSE2NEON : Added version 1.9.1 when building on macOS. 11.0.0a4 (relative to 11.0.0a3) -------- diff --git a/Cycles/config.py b/Cycles/config.py index 21011221d7..de00448ffa 100644 --- a/Cycles/config.py +++ b/Cycles/config.py @@ -2,7 +2,7 @@ "downloads" : [ - "https://github.com/blender/cycles/archive/refs/tags/v5.0.0.tar.gz", + "https://github.com/blender/cycles/archive/refs/tags/v5.1.0.tar.gz", ], @@ -12,6 +12,12 @@ "dependencies" : [ "Boost", "OpenJPEG", "OpenImageIO", "TBB", "Alembic", "Embree", "OpenColorIO", "OpenVDB", "OpenShadingLanguage", "OpenSubdiv", "OpenPGL", "LibWebP", "Zstandard" ], + "platform:macos" : { + + "extraDependencies" : [ "SSE2NEON" ], + + }, + "environment" : { # Needed because the build process runs oslc. diff --git a/Cycles/patches/0001-Cycles-Add-NodeType-get_type_names-for-iterating-all.patch b/Cycles/patches/0001-Cycles-Add-NodeType-get_type_names-for-iterating-all.patch new file mode 100644 index 0000000000..9f1dfb71af --- /dev/null +++ b/Cycles/patches/0001-Cycles-Add-NodeType-get_type_names-for-iterating-all.patch @@ -0,0 +1,180 @@ +From 677bae0b9376e7963d1c37bbf92b0db14ecfc0d8 Mon Sep 17 00:00:00 2001 +From: Brecht Van Lommel +Date: Wed, 1 Apr 2026 19:55:32 +0200 +Subject: [PATCH] Add nodetype::get_type_names() for iterating all node + types + +Node types are registered lazily to ensure base types are registered +before derived types, which is tricky to ensure with static +initialization. This meant there was no more global list of types. + +Preserve that behavior, and instead gather init functions on static +initialization. + +Ref #155818 + +Pull Request: https://projects.blender.org/blender/blender/pulls/156326 +--- + intern/cycles/graph/node_type.cpp | 68 ++++++++++++++++++++++++++-- + intern/cycles/graph/node_type.h | 7 +-- + intern/cycles/scene/shader_nodes.cpp | 5 ++ + intern/cycles/scene/shader_nodes.h | 1 + + 4 files changed, 73 insertions(+), 8 deletions(-) + +diff --git a/src/graph/node_type.cpp b/src/graph/node_type.cpp +index 16553582d2a..1cf5c5d31e1 100644 +--- a/src/graph/node_type.cpp ++++ b/src/graph/node_type.cpp +@@ -205,22 +205,65 @@ const SocketType *NodeType::find_output(ustring name) const + return nullptr; + } + +-/* Node Type Registry */ ++/* Node Type Registry ++ * ++ * We want to be able to find and enumerate types without the need for a centralized ++ * function for all node types. Due to static initialization order issues and guarded ++ * allocators, there are some subtle implementation choices. ++ * ++ * Only the init functions are gathered on static initialization, as registering the ++ * node type itself could otherwise happen in the wrong order for base and derived ++ * types, and Blender's guarded allocator may not have been initialized yet. ++ * ++ * The initialization functions are stored in std::vector without guarded allocation ++ * as that may not been properly initialized yet. */ + +-thread_mutex NodeType::types_mutex_; ++static thread_mutex &types_mutex() ++{ ++ static thread_mutex types_mutex_; ++ return types_mutex_; ++} + +-unordered_map &NodeType::types() ++static unordered_map &types() + { + static unordered_map _types; + return _types; + } + ++static thread_mutex &types_on_init_mutex() ++{ ++ static thread_mutex types_on_init_mutex_; ++ return types_on_init_mutex_; ++} ++ ++static std::vector &types_on_init() ++{ ++ static std::vector _types_on_init; ++ return _types_on_init; ++} ++ ++static void ensure_types_initialized() ++{ ++ thread_scoped_lock lock(types_on_init_mutex()); ++ for (const auto &init_func : types_on_init()) { ++ init_func(); ++ } ++ types_on_init().clear(); ++} ++ ++bool NodeType::register_on_init(const NodeType *(*init_func)()) ++{ ++ thread_scoped_lock lock(types_on_init_mutex()); ++ types_on_init().push_back(init_func); ++ return true; ++} ++ + NodeType *NodeType::add(const char *name_, CreateFunc create_, Type type_, const NodeType *base_) + { + const ustring name(name_); + + /* Types can be lazily registered from multiple threads. */ +- thread_scoped_lock lock(types_mutex_); ++ thread_scoped_lock lock(types_mutex()); + + if (types().find(name) != types().end()) { + LOG_ERROR << "Node type " << name_ << " registered twice"; +@@ -238,9 +281,24 @@ NodeType *NodeType::add(const char *name_, CreateFunc create_, Type type_, const + + const NodeType *NodeType::find(ustring name) + { +- thread_scoped_lock lock(types_mutex_); ++ ensure_types_initialized(); ++ ++ thread_scoped_lock lock(types_mutex()); + const unordered_map::iterator it = types().find(name); + return (it == types().end()) ? nullptr : &it->second; + } + ++vector NodeType::type_names() ++{ ++ ensure_types_initialized(); ++ ++ thread_scoped_lock lock(types_mutex()); ++ vector names; ++ for (const auto &pair : types()) { ++ names.push_back(pair.first); ++ } ++ ++ return names; ++} ++ + CCL_NAMESPACE_END +diff --git a/src/graph/node_type.h b/src/graph/node_type.h +index 572a3ab6d88..69515033b3d 100644 +--- a/src/graph/node_type.h ++++ b/src/graph/node_type.h +@@ -134,10 +134,9 @@ struct NodeType { + Type type = NONE, + const NodeType *base = nullptr); + static const NodeType *find(ustring name); ++ static vector type_names(); + +- private: +- static unordered_map &types(); +- static thread_mutex types_mutex_; ++ static bool register_on_init(const NodeType *(*init_func)()); + }; + + /* Node Definition Macros +@@ -155,6 +154,8 @@ struct NodeType { + #define NODE_DEFINE(structname) \ + const NodeType *structname::node_type_ = nullptr; \ + thread_mutex structname::node_type_mutex_; \ ++ static bool structname##_register_on_init = NodeType::register_on_init( \ ++ structname::get_node_type); \ + unique_ptr structname::create(const NodeType *) \ + { \ + return make_unique(); \ +diff --git a/src/scene/shader_nodes.cpp b/src/scene/shader_nodes.cpp +index f6935675f5e..9a5fb808a6c 100644 +--- a/src/scene/shader_nodes.cpp ++++ b/src/scene/shader_nodes.cpp +@@ -2070,6 +2070,11 @@ const NodeType *(&ConvertNode::get_node_types())[ConvertNode::MAX_TYPE][ConvertN + return node_types; + } + ++bool ConvertNode::register_on_init = NodeType::register_on_init([] { ++ ConvertNode::get_node_types(); ++ return static_cast(nullptr); ++}); ++ + unique_ptr ConvertNode::create(const NodeType *type) + { + return make_unique(type->inputs[0].type, type->outputs[0].type); +diff --git a/src/scene/shader_nodes.h b/src/scene/shader_nodes.h +index c7a6fbd4837..37300fbf033 100644 +--- a/src/scene/shader_nodes.h ++++ b/src/scene/shader_nodes.h +@@ -468,6 +468,7 @@ class ConvertNode : public ShaderNode { + static const int MAX_TYPE = 13; + static unique_ptr create(const NodeType *type); + static const NodeType *(&get_node_types())[MAX_TYPE][MAX_TYPE]; ++ static bool register_on_init; + }; + + class BsdfBaseNode : public ShaderNode { +-- +2.47.1.windows.2 + diff --git a/Cycles/patches/0002-Cycles-Support-geometry-motion-blur-with-an-even-num.patch b/Cycles/patches/0002-Cycles-Support-geometry-motion-blur-with-an-even-num.patch new file mode 100644 index 0000000000..c1d633c451 --- /dev/null +++ b/Cycles/patches/0002-Cycles-Support-geometry-motion-blur-with-an-even-num.patch @@ -0,0 +1,172 @@ +From 500936c42f6421a47ae61f102557da9f33cbb272 Mon Sep 17 00:00:00 2001 +From: Brecht Van Lommel +Date: Wed, 8 Apr 2026 15:33:16 +0200 +Subject: [PATCH] Support geometry motion blur with an even number of + steps + +Some of the math assume and uneven number of steps with one center step, +particularly for BVH2 while OptiX and Embree mostly worked. Tweak the +formulas so any number of steps works, even though we still have some +assumption of a "center" step used for things like adaptive subdivision. +--- + intern/cycles/kernel/geom/motion_curve.h | 14 ++++++++------ + intern/cycles/kernel/geom/motion_point.h | 7 ++++--- + intern/cycles/kernel/geom/motion_triangle.h | 14 ++++++++------ + intern/cycles/scene/object.cpp | 2 +- + 4 files changed, 21 insertions(+), 16 deletions(-) + +diff --git a/src/kernel/geom/motion_curve.h b/src/kernel/geom/motion_curve.h +index f6c0f2427c4..9236bd5ff19 100644 +--- a/src/kernel/geom/motion_curve.h ++++ b/src/kernel/geom/motion_curve.h +@@ -30,14 +30,15 @@ ccl_device_inline void motion_curve_keys_for_step_linear(KernelGlobals kg, + const int k1, + float4 keys[2]) + { +- if (step == numsteps) { ++ const int center_step = (numsteps - 1) / 2; ++ if (step == center_step) { + /* center step: regular key location */ + keys[0] = kernel_data_fetch(curve_keys, k0); + keys[1] = kernel_data_fetch(curve_keys, k1); + } + else { + /* center step is not stored in this array */ +- if (step > numsteps) { ++ if (step > center_step) { + step--; + } + +@@ -61,7 +62,7 @@ ccl_device_inline void motion_curve_keys_linear(KernelGlobals kg, + const int numverts = kernel_data_fetch(objects, object).numverts; + + /* figure out which steps we need to fetch and their interpolation factor */ +- const int maxstep = numsteps * 2; ++ const int maxstep = numsteps - 1; + const int step = min((int)(time * maxstep), maxstep - 1); + const float t = time * maxstep - step; + +@@ -91,7 +92,8 @@ ccl_device_inline void motion_curve_keys_for_step(KernelGlobals kg, + const int k3, + float4 keys[4]) + { +- if (step == numsteps) { ++ const int center_step = (numsteps - 1) / 2; ++ if (step == center_step) { + /* center step: regular key location */ + keys[0] = kernel_data_fetch(curve_keys, k0); + keys[1] = kernel_data_fetch(curve_keys, k1); +@@ -100,7 +102,7 @@ ccl_device_inline void motion_curve_keys_for_step(KernelGlobals kg, + } + else { + /* center step is not stored in this array */ +- if (step > numsteps) { ++ if (step > center_step) { + step--; + } + +@@ -128,7 +130,7 @@ ccl_device_inline void motion_curve_keys(KernelGlobals kg, + const int numverts = kernel_data_fetch(objects, object).numverts; + + /* figure out which steps we need to fetch and their interpolation factor */ +- const int maxstep = numsteps * 2; ++ const int maxstep = numsteps - 1; + const int step = min((int)(time * maxstep), maxstep - 1); + const float t = time * maxstep - step; + +diff --git a/src/kernel/geom/motion_point.h b/src/kernel/geom/motion_point.h +index ccde301c200..4e2ad0ad93f 100644 +--- a/src/kernel/geom/motion_point.h ++++ b/src/kernel/geom/motion_point.h +@@ -24,12 +24,13 @@ CCL_NAMESPACE_BEGIN + ccl_device_inline float4 motion_point_for_step( + KernelGlobals kg, int offset, const int numverts, const int numsteps, int step, const int prim) + { +- if (step == numsteps) { ++ const int center_step = (numsteps - 1) / 2; ++ if (step == center_step) { + /* center step: regular key location */ + return kernel_data_fetch(points, prim); + } + /* center step is not stored in this array */ +- if (step > numsteps) { ++ if (step > center_step) { + step--; + } + +@@ -49,7 +50,7 @@ ccl_device_inline float4 motion_point(KernelGlobals kg, + const int numverts = kernel_data_fetch(objects, object).numverts; + + /* figure out which steps we need to fetch and their interpolation factor */ +- const int maxstep = numsteps * 2; ++ const int maxstep = numsteps - 1; + const int step = min((int)(time * maxstep), maxstep - 1); + const float t = time * maxstep - step; + +diff --git a/src/kernel/geom/motion_triangle.h b/src/kernel/geom/motion_triangle.h +index dff17f5d38a..8159a840314 100644 +--- a/src/kernel/geom/motion_triangle.h ++++ b/src/kernel/geom/motion_triangle.h +@@ -32,15 +32,16 @@ ccl_device_inline void motion_triangle_verts_for_step(KernelGlobals kg, + int step, + float3 verts[3]) + { +- if (step == numsteps) { ++ const int center_step = (numsteps - 1) / 2; ++ if (step == center_step) { + /* center step: regular vertex location */ + verts[0] = kernel_data_fetch(tri_verts, tri_vindex.x); + verts[1] = kernel_data_fetch(tri_verts, tri_vindex.y); + verts[2] = kernel_data_fetch(tri_verts, tri_vindex.z); + } + else { +- /* center step not store in this array */ +- if (step > numsteps) { ++ /* center step not stored in this array */ ++ if (step > center_step) { + step--; + } + +@@ -62,14 +63,15 @@ ccl_device_inline void motion_triangle_normals_for_step(KernelGlobals kg, + int step, + float3 normals[3]) + { +- const bool is_center_step = step == numsteps; ++ const int center_step = (numsteps - 1) / 2; ++ const bool is_center_step = step == center_step; + if (is_center_step) { + /* Center step in the regular attribute. */ + offset = kernel_data_fetch(objects, object).normal_attr_offset; + } + else { + /* Other steps in the motion attribute, compensate for missing center step. */ +- if (step > numsteps) { ++ if (step > center_step) { + step--; + } + } +@@ -111,7 +113,7 @@ ccl_device_inline void motion_triangle_compute_info(KernelGlobals kg, + *numsteps = kernel_data_fetch(objects, object).num_geom_steps; + + /* Figure out which steps we need to fetch and their interpolation factor. */ +- const int maxstep = *numsteps * 2; ++ const int maxstep = *numsteps - 1; + *step = min((int)(time * maxstep), maxstep - 1); + *t = time * maxstep - *step; + +diff --git a/src/scene/object.cpp b/src/scene/object.cpp +index f7034dc0167..94ccdb14523 100644 +--- a/src/scene/object.cpp ++++ b/src/scene/object.cpp +@@ -645,7 +645,7 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s + kobject.dupli_generated[2] = ob->dupli_generated[2]; + kobject.dupli_uv[0] = ob->dupli_uv[0]; + kobject.dupli_uv[1] = ob->dupli_uv[1]; +- kobject.num_geom_steps = (geom->get_motion_steps() - 1) / 2; ++ kobject.num_geom_steps = geom->get_motion_steps(); + kobject.num_tfm_steps = ob->motion.size(); + kobject.numverts = object_num_motion_verts(geom); + kobject.numprims = (geom->is_mesh() || geom->is_volume()) ? +-- +2.47.1.windows.2 + diff --git a/Cycles/patches/0003-Fix-Cycles-Incorrect-BVH2-asserts.patch b/Cycles/patches/0003-Fix-Cycles-Incorrect-BVH2-asserts.patch new file mode 100644 index 0000000000..0448843188 --- /dev/null +++ b/Cycles/patches/0003-Fix-Cycles-Incorrect-BVH2-asserts.patch @@ -0,0 +1,100 @@ +From e3043ef2e116918d1dff3ff27ad3ef594045fcc5 Mon Sep 17 00:00:00 2001 +From: Brecht Van Lommel +Date: Wed, 8 Apr 2026 17:22:39 +0200 +Subject: [PATCH] Fix: Cycles: Incorrect BVH2 asserts + +Need to mask by PRIMITIVE_ALL to handle PRIMITIVE_PACK_SEGMENT. +--- + intern/cycles/kernel/bvh/local.h | 6 ++++-- + intern/cycles/kernel/bvh/traversal.h | 1 - + intern/cycles/kernel/bvh/volume.h | 6 ++++-- + intern/cycles/kernel/bvh/volume_all.h | 6 ++++-- + 4 files changed, 12 insertions(+), 7 deletions(-) + +diff --git a/src/kernel/bvh/local.h b/src/kernel/bvh/local.h +index 3abb1360e03..ad7dd0bd1d9 100644 +--- a/src/kernel/bvh/local.h ++++ b/src/kernel/bvh/local.h +@@ -137,7 +137,8 @@ ccl_device_inline + case PRIMITIVE_TRIANGLE: { + /* intersect ray against primitive */ + for (; prim_addr < prim_addr2; prim_addr++) { +- kernel_assert(kernel_data_fetch(prim_type, prim_addr) == type); ++ kernel_assert((kernel_data_fetch(prim_type, prim_addr) & PRIMITIVE_ALL) == ++ (type & PRIMITIVE_ALL)); + + /* Only intersect with matching object, for instanced objects we + * already know we are only intersecting the right object. */ +@@ -173,7 +174,8 @@ ccl_device_inline + case PRIMITIVE_MOTION_TRIANGLE: { + /* intersect ray against primitive */ + for (; prim_addr < prim_addr2; prim_addr++) { +- kernel_assert(kernel_data_fetch(prim_type, prim_addr) == type); ++ kernel_assert((kernel_data_fetch(prim_type, prim_addr) & PRIMITIVE_ALL) == ++ (type & PRIMITIVE_ALL)); + + /* Only intersect with matching object, for instanced objects we + * already know we are only intersecting the right object. */ +diff --git a/src/kernel/bvh/traversal.h b/src/kernel/bvh/traversal.h +index 86030691a14..701de208b67 100644 +--- a/src/kernel/bvh/traversal.h ++++ b/src/kernel/bvh/traversal.h +@@ -121,7 +121,6 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, + + /* primitive intersection */ + for (; prim_addr < prim_addr2; prim_addr++) { +- kernel_assert(kernel_data_fetch(prim_type, prim_addr) == type); + + const int prim_object = (object == OBJECT_NONE) ? + kernel_data_fetch(prim_object, prim_addr) : +diff --git a/src/kernel/bvh/volume.h b/src/kernel/bvh/volume.h +index 3bed9ed1755..d1de5020bb6 100644 +--- a/src/kernel/bvh/volume.h ++++ b/src/kernel/bvh/volume.h +@@ -126,7 +126,8 @@ ccl_device_inline + case PRIMITIVE_TRIANGLE: { + /* intersect ray against primitive */ + for (; prim_addr < prim_addr2; prim_addr++) { +- kernel_assert(kernel_data_fetch(prim_type, prim_addr) == type); ++ kernel_assert((kernel_data_fetch(prim_type, prim_addr) & PRIMITIVE_ALL) == ++ (type & PRIMITIVE_ALL)); + /* only primitives from volume object */ + const int prim_object = (object == OBJECT_NONE) ? + kernel_data_fetch(prim_object, prim_addr) : +@@ -147,7 +148,8 @@ ccl_device_inline + case PRIMITIVE_MOTION_TRIANGLE: { + /* intersect ray against primitive */ + for (; prim_addr < prim_addr2; prim_addr++) { +- kernel_assert(kernel_data_fetch(prim_type, prim_addr) == type); ++ kernel_assert((kernel_data_fetch(prim_type, prim_addr) & PRIMITIVE_ALL) == ++ (type & PRIMITIVE_ALL)); + /* only primitives from volume object */ + const int prim_object = (object == OBJECT_NONE) ? + kernel_data_fetch(prim_object, prim_addr) : +diff --git a/src/kernel/bvh/volume_all.h b/src/kernel/bvh/volume_all.h +index 32ef79e2ba4..0a3d157ab3e 100644 +--- a/src/kernel/bvh/volume_all.h ++++ b/src/kernel/bvh/volume_all.h +@@ -126,7 +126,8 @@ ccl_device_inline + case PRIMITIVE_TRIANGLE: { + /* intersect ray against primitive */ + for (; prim_addr < prim_addr2; prim_addr++) { +- kernel_assert(kernel_data_fetch(prim_type, prim_addr) == type); ++ kernel_assert((kernel_data_fetch(prim_type, prim_addr) & PRIMITIVE_ALL) == ++ (type & PRIMITIVE_ALL)); + /* only primitives from volume object */ + const int prim_object = (object == OBJECT_NONE) ? + kernel_data_fetch(prim_object, prim_addr) : +@@ -163,7 +164,8 @@ ccl_device_inline + case PRIMITIVE_MOTION_TRIANGLE: { + /* intersect ray against primitive */ + for (; prim_addr < prim_addr2; prim_addr++) { +- kernel_assert(kernel_data_fetch(prim_type, prim_addr) == type); ++ kernel_assert((kernel_data_fetch(prim_type, prim_addr) & PRIMITIVE_ALL) == ++ (type & PRIMITIVE_ALL)); + /* only primitives from volume object */ + const int prim_object = (object == OBJECT_NONE) ? + kernel_data_fetch(prim_object, prim_addr) : +-- +2.47.1.windows.2 + diff --git a/Cycles/patches/cudaBinaries.patch b/Cycles/patches/cudaBinaries.patch index e4e8e81587..845c729155 100644 --- a/Cycles/patches/cudaBinaries.patch +++ b/Cycles/patches/cudaBinaries.patch @@ -1,6 +1,6 @@ --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -141,7 +141,7 @@ +@@ -144,7 +144,7 @@ option(WITH_CYCLES_CUDA_BINARIES "Build Cycles NVIDIA CUDA binaries" OFF) set(CYCLES_CUDA_BINARIES_ARCH diff --git a/SSE2NEON/config.py b/SSE2NEON/config.py new file mode 100644 index 0000000000..a508a5e226 --- /dev/null +++ b/SSE2NEON/config.py @@ -0,0 +1,34 @@ +{ + + "downloads" : [ + + "https://github.com/DLTcollab/sse2neon/archive/refs/tags/v1.9.1.tar.gz" + + ], + + "url" : "https://github.com/DLTcollab/sse2neon", + + "license" : "LICENSE", + + "commands" : [ + + "cmake -E copy sse2neon.h {buildDir}/include/sse2neon.h", + + ], + + # sse2neon is only required on macOS + "enabled" : False, + + "platform:macos" : { + + "enabled" : True, + + }, + + "manifest" : [ + + "include/sse2neon.h", + + ], + +} diff --git a/build.py b/build.py index 0ec0bbffd4..9f03156fd8 100755 --- a/build.py +++ b/build.py @@ -39,6 +39,9 @@ - enabled : May be set to `False` to disable a project entirely. This is only expected to be useful in conjunction with platform overrides or variants. +- extraDependencies : List of projects to be appended to `dependencies` + when the config is loaded. Useful for specifying additional + platform-specific dependencies via a platform override. ### Packaging @@ -207,6 +210,9 @@ def __loadConfigs( variables, variants ) : for variantProject, variant in variants.items() : __applyConfigOverrides( config, "variant:{}:{}".format( variantProject, variant ) ) __applyConfigOverrides( config, { "darwin": "platform:macos", "win32": "platform:windows" }.get( sys.platform, "platform:linux" ) ) + if "extraDependencies" in config : + config.setdefault( "dependencies", [] ).extend( config["extraDependencies"] ) + if config.get( "enabled", True ) : configs[project] = config