From 9ebfa830f63d77e3f87b65a554051c08a73c49b8 Mon Sep 17 00:00:00 2001 From: be5invis Date: Fri, 27 Oct 2023 18:20:43 -0700 Subject: [PATCH] Optimize build speed --- font-src/gen/build-font.mjs | 9 +- font-src/glyphs/auto-build/composite.ptl | 4 +- font-src/glyphs/auto-build/transformed.ptl | 4 +- font-src/glyphs/index.ptl | 4 +- .../glyphs/letter/latin-ext/orthography.ptl | 2 +- .../glyphs/letter/latin-ext/upper-aa-ao.ptl | 2 +- font-src/glyphs/space/index.ptl | 2 +- .../glyphs/symbol/geometric/ballot-box.ptl | 2 +- .../glyphs/symbol/punctuation/pilcrow.ptl | 2 +- .../glyphs/symbol/punctuation/section.ptl | 2 +- font-src/index.mjs | 8 +- font-src/otl/gsub-cv-ss.ptl | 11 ++- font-src/support/glyph-block.mjs | 86 +++++++++++++---- utility/amend-readme/index.mjs | 1 + verdafile.mjs | 95 +++++++++++++------ 15 files changed, 167 insertions(+), 67 deletions(-) diff --git a/font-src/gen/build-font.mjs b/font-src/gen/build-font.mjs index 8207a42c9..c02672392 100644 --- a/font-src/gen/build-font.mjs +++ b/font-src/gen/build-font.mjs @@ -13,9 +13,11 @@ export async function buildFont(argv, para) { const baseFont = CreateEmptyFont(argv); assignFontNames(baseFont, para.naming, para.isQuasiProportional); + // Build glyphs const gs = buildGlyphs(para); copyFontMetrics(gs.fontMetrics, baseFont); + // Build OTL const otl = buildOtl(para, gs.glyphStore); // Regulate @@ -25,12 +27,13 @@ export async function buildFont(argv, para) { for (let p = start; p <= end; p++) excludeChars.add(p); } } + // Finalize (like geometry conversion) const cache = await Caching.load(argv.iCache, argv.menu.version, argv.cacheFreshAgeKey); const finalGs = finalizeFont(cache, para, gs.glyphStore, excludeChars, otl); - if (cache.isUpdated()) { - await Caching.save(argv.oCache, argv.menu.version, cache, true); - } + if (cache.isUpdated()) await Caching.save(argv.oCache, argv.menu.version, cache, true); + + // Convert to TTF const font = await convertOtd(baseFont, otl, finalGs); const ttfaControls = await generateTtfaControls(finalGs, font.glyphs); return { font, glyphStore: finalGs, cacheUpdated: cache.isUpdated(), ttfaControls }; diff --git a/font-src/glyphs/auto-build/composite.ptl b/font-src/glyphs/auto-build/composite.ptl index 6a5d6b9d6..2fee55546 100644 --- a/font-src/glyphs/auto-build/composite.ptl +++ b/font-src/glyphs/auto-build/composite.ptl @@ -253,7 +253,7 @@ glyph-block AutoBuild-Enclosure : begin define ItalicSpacing : object gniPrefix 'i' getPara : function[pp digits rows width] : begin - define pp1 : pp.reinit : function [a] : begin + define pp1 : pp.createFork : function [a] : begin set a.shape.slope 'italic' set a.shape.slopeAngle : mix (para.slopeAngle || 0) 15 (95 / 150) return : StandardSpacing.getPara pp1 digits rows width @@ -261,7 +261,7 @@ glyph-block AutoBuild-Enclosure : begin define SansSerifSpacing : object gniPrefix 'ss' getPara : function [pp digits rows width] : begin - define pp1 : pp.reinit : function [a] : begin + define pp1 : pp.createFork : function [a] : begin set a.shape.serifs 'sans' return : StandardSpacing.getPara pp1 digits rows width diff --git a/font-src/glyphs/auto-build/transformed.ptl b/font-src/glyphs/auto-build/transformed.ptl index 7620cbb85..f225de2f9 100644 --- a/font-src/glyphs/auto-build/transformed.ptl +++ b/font-src/glyphs/auto-build/transformed.ptl @@ -674,7 +674,7 @@ glyph-block Autobuild-Transformed : begin : where : [createReversed _records] : begin local { records relSets targetNameMap } : extendRelatedGlyphs 'reversed' _records local pendingGlyphs : records.map : [record] => record.1 - local forkedPara : para.reinit : function p : begin + local forkedPara : para.createFork : function p : begin set p.shape.slopeAngle (-p.shape.slopeAngle) local miniatureFont : Fork pendingGlyphs forkedPara @@ -702,7 +702,7 @@ glyph-block Autobuild-Transformed-Mathematical : begin define [createMathDerivedSeriesImpl groupName tfm _records] : begin local { records relSets targetNameMap } : extendRelatedGlyphs groupName _records local pendingGlyphs : records.map : [record] => record.1 - local forkedPara : para.reinit tfm + local forkedPara : para.createFork tfm local forked : Fork pendingGlyphs forkedPara foreach {unicode glyphid} [items-of records] : if [not : query-glyph targetNameMap.(glyphid)] create-glyph targetNameMap.(glyphid) unicode : glyph-proc diff --git a/font-src/glyphs/index.ptl b/font-src/glyphs/index.ptl index 6190c3e5c..504bedb88 100644 --- a/font-src/glyphs/index.ptl +++ b/font-src/glyphs/index.ptl @@ -15,7 +15,7 @@ $$include '../meta/macros.ptl' export : define [buildGlyphs para recursive] : begin # Execution and dependency management - local $Exec$ : new GlyphBuildExecutor + local $Exec$ : new GlyphBuildExecutor recursive define [glyph-is-needed name] : [not recursive] || [recursive.glyphIsNeeded name] # Initialize glyph store @@ -138,7 +138,7 @@ export : define [buildGlyphs para recursive] : begin run-glyph-module "./auto-build/transformed.mjs" run-glyph-module "./auto-build/composite.mjs" - foreach [gb : items-of $Exec$.pendingGlyphBlocks] : gb.resolve + $Exec$.executePendingBlocks Gr.linkSuffixPairGr glyphStore 'NWID' 'WWID' Gr.Nwid Gr.Wwid Gr.linkSuffixPairGr glyphStore 'lnum' 'onum' Gr.Lnum Gr.Onum diff --git a/font-src/glyphs/letter/latin-ext/orthography.ptl b/font-src/glyphs/letter/latin-ext/orthography.ptl index 83529c1a3..8847ebe80 100644 --- a/font-src/glyphs/letter/latin-ext/orthography.ptl +++ b/font-src/glyphs/letter/latin-ext/orthography.ptl @@ -2,7 +2,7 @@ $$include '../../../meta/macros.ptl' glyph-module -glyph-block Letter-Cyrillic-Orthography : begin +glyph-block Letter-Latin-Orthography : begin glyph-block-import Common-Derivatives # orthographic-italic 'f_i' 0xFB01 diff --git a/font-src/glyphs/letter/latin-ext/upper-aa-ao.ptl b/font-src/glyphs/letter/latin-ext/upper-aa-ao.ptl index 706388d2b..b8ff60cca 100644 --- a/font-src/glyphs/letter/latin-ext/upper-aa-ao.ptl +++ b/font-src/glyphs/letter/latin-ext/upper-aa-ao.ptl @@ -6,7 +6,7 @@ import [bitOr] from"../../../support/util/mask-bit.mjs" glyph-module -glyph-block Letter-Latin-Upper-AE-OE : begin +glyph-block Letter-Latin-Upper-AA-AO : begin glyph-block-import CommonShapes glyph-block-import Common-Derivatives glyph-block-import Letter-Latin-Upper-F : EFVJutLength diff --git a/font-src/glyphs/space/index.ptl b/font-src/glyphs/space/index.ptl index 362f28dd1..3fc15fda7 100644 --- a/font-src/glyphs/space/index.ptl +++ b/font-src/glyphs/space/index.ptl @@ -5,7 +5,7 @@ import [Joining] from"../../support/gr.mjs" glyph-module -glyph-block Symbol-Mosaic-NotDef : begin +glyph-block Spaces : begin glyph-block-import CommonShapes glyph-block-import Common-Derivatives diff --git a/font-src/glyphs/symbol/geometric/ballot-box.ptl b/font-src/glyphs/symbol/geometric/ballot-box.ptl index e777023c6..d8150a0de 100644 --- a/font-src/glyphs/symbol/geometric/ballot-box.ptl +++ b/font-src/glyphs/symbol/geometric/ballot-box.ptl @@ -5,7 +5,7 @@ import [DesignParameters] from"../../../meta/aesthetics.mjs" glyph-module -glyph-block Symbol-Geometric-Dice : for-width-kinds WideWidth1 +glyph-block Symbol-Geometric-Ballot-Box : for-width-kinds WideWidth1 glyph-block-import CommonShapes glyph-block-import Common-Derivatives glyph-block-import Symbol-Geometric-Shared : GeometricDim UnicodeWeightGrade GeometricSizes diff --git a/font-src/glyphs/symbol/punctuation/pilcrow.ptl b/font-src/glyphs/symbol/punctuation/pilcrow.ptl index e59003233..9b5fd0d5f 100644 --- a/font-src/glyphs/symbol/punctuation/pilcrow.ptl +++ b/font-src/glyphs/symbol/punctuation/pilcrow.ptl @@ -5,7 +5,7 @@ import [mix linreg clamp fallback] from"../../../support/utils.mjs" glyph-module -glyph-block Symbol-Punctuation-ParagraphAndSection : begin +glyph-block Symbol-Punctuation-Pilcrow : begin glyph-block-import CommonShapes glyph-block-import Common-Derivatives diff --git a/font-src/glyphs/symbol/punctuation/section.ptl b/font-src/glyphs/symbol/punctuation/section.ptl index b217a1705..3faf789e3 100644 --- a/font-src/glyphs/symbol/punctuation/section.ptl +++ b/font-src/glyphs/symbol/punctuation/section.ptl @@ -5,7 +5,7 @@ import [mix linreg clamp fallback] from"../../../support/utils.mjs" glyph-module -glyph-block Symbol-Punctuation-ParagraphAndSection : begin +glyph-block Symbol-Punctuation-Section : begin glyph-block-import CommonShapes glyph-block-import Common-Derivatives diff --git a/font-src/index.mjs b/font-src/index.mjs index f91c58919..0b89328af 100644 --- a/font-src/index.mjs +++ b/font-src/index.mjs @@ -70,16 +70,16 @@ async function getParameters() { }; return para; } - function reinit(argv) { + function paraT(argv) { const para = createParaImpl(argv); - para.reinit = function (tf) { + para.createFork = function (tf) { const argv1 = deepClone(argv); tf(argv1, argv); - return reinit(argv1); + return paraT(argv1); }; return para; } - return reinit; + return paraT; } async function tryParseToml(str) { try { diff --git a/font-src/otl/gsub-cv-ss.ptl b/font-src/otl/gsub-cv-ss.ptl index d46647473..f6ad2fe01 100644 --- a/font-src/otl/gsub-cv-ss.ptl +++ b/font-src/otl/gsub-cv-ss.ptl @@ -92,11 +92,11 @@ export : define [buildCVSS gsub para glyphStore] : begin if (variant.tag && variant.rank) : cvGrs.push variant cvGrs.sort AnyCv.compare - foreach gr [items-of cvGrs] : begin - local cvAlt : [cvs.get gr.tag].createAlternateSubst - foreach {gn glyph} [glyphStore.namedEntries] : if [not : CvDecompose.get glyph] : begin + foreach {gn glyph} [glyphStore.namedEntries] : if [not : CvDecompose.get glyph] : begin + foreach gr [items-of cvGrs] : begin local subst : gr.get glyph if (subst && subst != gn) : begin + local cvAlt : [cvs.get gr.tag].createAlternateSubst if [not cvAlt.substitutions.(gn)] : set cvAlt.substitutions.(gn) { } set cvAlt.substitutions.(gn).(gr.rank - 1) : glyphStore.ensureExists subst @@ -114,9 +114,12 @@ export : define [buildCVSS gsub para glyphStore] : begin foreach gr [items-of ssGrs] : begin local cvSingle : [cvs.get gr.tag].createSingleSubstFor gr.rank feature.addLookup cvSingle - foreach {gn glyph} [glyphStore.namedEntries] : if [not : CvDecompose.get glyph] : begin + + foreach {gn glyph} [glyphStore.namedEntries] : if [not : CvDecompose.get glyph] : begin + foreach gr [items-of ssGrs] : begin local subst : gr.get glyph if (subst && subst != gn) : begin + local cvSingle : [cvs.get gr.tag].createSingleSubstFor gr.rank set cvSingle.substitutions.(gn) : glyphStore.ensureExists subst do "Cleanup and link dependency" diff --git a/font-src/support/glyph-block.mjs b/font-src/support/glyph-block.mjs index 4653e8a3f..74a691a09 100644 --- a/font-src/support/glyph-block.mjs +++ b/font-src/support/glyph-block.mjs @@ -1,35 +1,57 @@ export class GlyphBuildExecutor { - constructor() { - this.currentBlockName = null; + constructor(recursiveBuildFilter) { + this.recursiveBuildFilter = recursiveBuildFilter; + this.currentBlockId = null; this.dependencyManager = new DependencyManager(); this.pendingGlyphBlocks = []; this.glyphBlockStore = {}; } setGlyphToBlockDependency(glyph) { - if (this.currentBlockName) { - this.dependencyManager.glyphToBlock.set(glyph, this.currentBlockName); + if (this.currentBlockId) { + this.dependencyManager.glyphToBlock.set(glyph, this.currentBlockId); + + let s = this.dependencyManager.blockToGlyph.get(this.currentBlockId); + if (!s) { + s = new Set(); + this.dependencyManager.blockToGlyph.set(this.currentBlockId, s); + } + s.add(glyph); } } - defineGlyphBlock(capture, blockName, body) { - const block = new GlyphBlock(capture, this, blockName, body); - this.glyphBlockStore[blockName] = block; + executePendingBlocks() { + // if (!this.recursiveBuildFilter) { + for (const block of this.pendingGlyphBlocks) block.resolve(); + // } else { + // for (const block of this.pendingGlyphBlocks) + // if (this.recursiveBuildFilter.blockIsNeeded(block.id)) block.resolve(); + // } + } + defineGlyphBlock(capture, id, body) { + const block = new GlyphBlock(capture, this, id, body); + if (this.glyphBlockStore[id]) throw new Error(`Duplicate glyph block: ${id}`); + this.glyphBlockStore[id] = block; this.pendingGlyphBlocks.push(block); } } export class RecursiveBuildFilter { - constructor(glyphIdFilter) { + constructor(glyphIdFilter, blockIdFilter) { this.glyphIdFilter = glyphIdFilter; + this.blockIdFilter = blockIdFilter; } glyphIsNeeded(id) { return this.glyphIdFilter.has(id); } + blockIsNeeded(id) { + return this.blockIdFilter.has(id); + } } export class DependencyManager { constructor() { this.glyphToGlyph = new WeakMap(); this.glyphToBlock = new WeakMap(); + this.blockToGlyph = new Map(); } addDependency(dependent, dependency) { let s = this.glyphToGlyph.get(dependent); @@ -39,12 +61,31 @@ export class DependencyManager { } s.add(dependency); } - traverseDependencies(glyphs) { + + traverseGlyphDependenciesImpl(glyphs, fBlockwiseExpand) { let state = new Map(); const PENDING = 1, CHECKED = 2; + for (const glyph of glyphs) state.set(glyph, PENDING); + // When fBlockwiseExpand is true, we need to expand the initial glyph set + // to include all glyphs in the same block. + if (fBlockwiseExpand) { + let blocks = new Set(); + for (const glyph of glyphs) { + let b = this.glyphToBlock.get(glyph); + if (b) blocks.add(b); + } + for (const b of blocks) { + const glyphs = this.blockToGlyph.get(b); + if (glyphs) { + for (const g of glyphs) state.set(g, PENDING); + } + } + } + + // Traverse the dependency graph for (;;) { let found = false; for (const [glyph, s] of state) { @@ -59,11 +100,24 @@ export class DependencyManager { if (!found) break; } + return state; + } + + traverseDependencies(glyphs) { + const gGlyphGraph = this.traverseGlyphDependenciesImpl(glyphs, false); + const gBlockGraph = this.traverseGlyphDependenciesImpl(glyphs, true); + let glyphIdFilter = new Set(); - for (const g of state.keys()) { + let blockIdFilter = new Set(); + for (const g of gGlyphGraph.keys()) { if (g.identifier) glyphIdFilter.add(g.identifier); } - return new RecursiveBuildFilter(glyphIdFilter); + for (const g of gBlockGraph.keys()) { + let b = this.glyphToBlock.get(g); + if (b) blockIdFilter.add(b); + } + + return new RecursiveBuildFilter(glyphIdFilter, blockIdFilter); } } @@ -71,18 +125,18 @@ export class GlyphBlock { constructor(capture, execState, blockName, body) { this.capture = capture; this.execState = execState; - this.blockName = blockName; + this.id = blockName; this.body = body; this.resolved = 0; this.exports = {}; } resolve() { if (this.resolved == 2) return this.exports; - if (this.resolved == 1) throw new Error(`Circular dependency detected: ${this.blockName}`); + if (this.resolved == 1) throw new Error(`Circular dependency detected: ${this.id}`); this.resolved = 1; - const prevBlockName = this.execState.currentBlockName; - this.execState.currentBlockName = this.blockName; + const prevBlockName = this.execState.currentBlockId; + this.execState.currentBlockId = this.id; const pendingApplications = []; const ExportCapture = fnObj => { @@ -95,7 +149,7 @@ export class GlyphBlock { this.body(this.capture, ExportCapture); for (const f of pendingApplications) f(); - this.execState.currentBlockName = prevBlockName; + this.execState.currentBlockId = prevBlockName; this.resolved = 2; return this.exports; } diff --git a/utility/amend-readme/index.mjs b/utility/amend-readme/index.mjs index 93defb62d..4d3f0efb0 100644 --- a/utility/amend-readme/index.mjs +++ b/utility/amend-readme/index.mjs @@ -112,6 +112,7 @@ async function processSsStyles() { if (!gr.rank) continue; md.log(` - \`${gr.tag}\`: Set character variant to “${gr.description}”.`); } + md.log(` - Other build plans’ configuration, using \`inherits = "buildPlans."\`.`); return md; } diff --git a/verdafile.mjs b/verdafile.mjs index 68ba3684d..4e34841d3 100644 --- a/verdafile.mjs +++ b/verdafile.mjs @@ -31,7 +31,7 @@ const DIST_SUPER_TTC = "dist/.super-ttc"; const ARCHIVE_DIR = "release-archives"; const PATEL_C = ["node", "node_modules/patel/bin/patel-c"]; -const TTCIZE = ["node", "node_modules/otb-ttc-bundle/bin/otb-ttc-bundle"]; +const MAKE_TTC = ["node", "node_modules/otb-ttc-bundle/bin/otb-ttc-bundle"]; const SEVEN_ZIP = process.env.SEVEN_ZIP_PATH || "7z"; const TTFAUTOHINT = process.env.TTFAUTOHINT_PATH || "ttfautohint"; @@ -130,13 +130,25 @@ const BuildPlans = computed("metadata:build-plans", async target => { const [rp] = await target.need(RawPlans); const rawBuildPlans = rp.buildPlans; + // Initialize build plans const returnBuildPlans = {}; - const fileNameToBpMap = {}; for (const prefix in rawBuildPlans) { const bp = { ...rawBuildPlans[prefix] }; - validateAndShimBuildPlans(prefix, bp, rp.weights, rp.slopes, rp.widths); + if (!bp.family) fail(`Build plan for ${prefix} does not have a family name. Exit.`); bp.webfontFormats = bp["webfont-formats"] || defaultWebFontFormats; bp.targets = []; + returnBuildPlans[prefix] = bp; + } + + // Resolve WWS, including inheritance and default config + for (const prefix in rawBuildPlans) { + resolveWws(prefix, returnBuildPlans, rp); + } + + // Create file name to BP mapping + const fileNameToBpMap = {}; + for (const prefix in rawBuildPlans) { + const bp = returnBuildPlans[prefix]; const weights = bp.weights, slopes = bp.slopes, widths = bp.widths; @@ -157,31 +169,31 @@ const BuildPlans = computed("metadata:build-plans", async target => { function linkSpacingDerivableBuildPlans(bps) { for (const pfxTo in bps) { - const planTo = bps[pfxTo]; - const planToVal = rectifyPlanForSpacingDerivation(planTo); - if (blockSpacingDerivation(planTo)) continue; - if (!isLinkDeriveToSpacing(planTo.spacing)) continue; + const bpTo = bps[pfxTo]; + if (blockSpacingDerivation(bpTo)) continue; + if (!isDeriveToSpacing(bpTo.spacing)) continue; for (const pfxFrom in bps) { - const planFrom = bps[pfxFrom]; - if (!isLinkDeriveFromSpacing(planFrom.spacing)) continue; - const planFromVal = rectifyPlanForSpacingDerivation(planFrom); - if (!deepEqual(planToVal, planFromVal)) continue; - planTo.spacingDeriveFrom = pfxFrom; + const bpFrom = bps[pfxFrom]; + if (!isDeriveFromSpacing(bpFrom.spacing)) continue; + if (!spacingDeriveCompatible(pfxTo, bpTo, pfxFrom, bpFrom)) continue; + bpTo.spacingDeriveFrom = pfxFrom; } } } - function blockSpacingDerivation(bp) { return !!bp["compatibility-ligatures"]; } -function isLinkDeriveToSpacing(spacing) { +function isDeriveToSpacing(spacing) { return spacing === "term" || spacing === "fontconfig-mono" || spacing === "fixed"; } -function isLinkDeriveFromSpacing(spacing) { +function isDeriveFromSpacing(spacing) { return !spacing || spacing === "normal"; } - -function rectifyPlanForSpacingDerivation(p) { +function spacingDeriveCompatible(pfxTo, bpTo, pfxFrom, bpFrom) { + // If the two build plans are the same, then they are compatible. + return deepEqual(rectifyPlanForSpacingDerive(bpTo), rectifyPlanForSpacingDerive(bpFrom)); +} +function rectifyPlanForSpacingDerive(p) { return { ...p, family: "#Validation", @@ -749,7 +761,7 @@ const GlyfTtc = file.make( async function buildCompositeTtc(out, inputs) { const inputPaths = inputs.map(f => f.full); echo.action(echo.hl.command(`Create TTC`), out.full, echo.hl.operator("<-"), inputPaths); - await absolutelySilently.run(TTCIZE, ["-o", out.full], inputPaths); + await absolutelySilently.run(MAKE_TTC, ["-o", out.full], inputPaths); } async function buildGlyphSharingTtc(target, parts, out) { @@ -757,7 +769,7 @@ async function buildGlyphSharingTtc(target, parts, out) { const [ttfInputs] = await target.need(parts.map(part => BuildNoGcTtf(part.dir, part.file))); const ttfInputPaths = ttfInputs.map(p => p.full); echo.action(echo.hl.command(`Create TTC`), out.full, echo.hl.operator("<-"), ttfInputPaths); - await silently.run(TTCIZE, "-u", ["-o", out.full], ttfInputPaths); + await silently.run(MAKE_TTC, "-u", ["-o", out.full], ttfInputPaths); } /////////////////////////////////////////////////////////// @@ -1179,21 +1191,48 @@ const Parameters = task(`meta:parameters`, async target => { /////////////////////////////////////////////////////////// // Build plan validation -function validateAndShimBuildPlans(prefix, bp, dWeights, dSlopes, dWidths) { - if (!bp.family) { - fail(`Build plan for ${prefix} does not have a family name. Exit.`); - } + +function resolveWws(bpName, buildPlans, defaultConfig) { + const bp = buildPlans[bpName]; + if (!bp) fail(`Build plan ${bpName} not found.`); + if (!bp.slopes && bp.slants) { - echo.warn( - `Build plan for ${prefix} uses legacy "slants" to define slopes. ` + + fail( + `Build plan for ${bpName} uses legacy "slants" to define slopes. ` + `Use "slopes" instead.` ); } - bp.weights = shimBpAspect("weights", bp.weights, dWeights); - bp.slopes = shimBpAspect("slopes", bp.slopes || bp.slants, dSlopes); - bp.widths = shimBpAspect("widths", bp.widths, dWidths); + bp.weights = resolveWwsAspect("weights", bpName, buildPlans, defaultConfig, []); + bp.widths = resolveWwsAspect("widths", bpName, buildPlans, defaultConfig, []); + bp.slopes = resolveWwsAspect("slopes", bpName, buildPlans, defaultConfig, []); } + +function resolveWwsAspect(aspectName, bpName, buildPlans, defaultConfig, deps) { + const bp = buildPlans[bpName]; + if (!bp) fail(`Build plan ${bpName} not found.`); + + if (bp[aspectName]) { + return shimBpAspect(aspectName, bp[aspectName], defaultConfig[aspectName]); + } else if (bp[`${aspectName}-inherits`]) { + const inheritedPlanName = bp[`${aspectName}-inherits`]; + const inheritedPlan = buildPlans[inheritedPlanName]; + if (deps.includes(inheritedPlan)) + fail(`Circular dependency detected when resolving ${aspectName} of ${bp.family}.`); + + const updatedDes = [...deps, bpName]; + return resolveWwsAspect( + aspectName, + inheritedPlanName, + buildPlans, + defaultConfig, + updatedDes + ); + } else { + return defaultConfig[aspectName]; + } +} + function shimBpAspect(aspectName, aspect, defaultAspect) { if (!aspect) return defaultAspect; const result = {};