Optimize build speed

This commit is contained in:
be5invis 2023-10-27 18:20:43 -07:00
parent 2ec275667e
commit 9ebfa830f6
15 changed files with 167 additions and 67 deletions

View file

@ -13,9 +13,11 @@ export async function buildFont(argv, para) {
const baseFont = CreateEmptyFont(argv); const baseFont = CreateEmptyFont(argv);
assignFontNames(baseFont, para.naming, para.isQuasiProportional); assignFontNames(baseFont, para.naming, para.isQuasiProportional);
// Build glyphs
const gs = buildGlyphs(para); const gs = buildGlyphs(para);
copyFontMetrics(gs.fontMetrics, baseFont); copyFontMetrics(gs.fontMetrics, baseFont);
// Build OTL
const otl = buildOtl(para, gs.glyphStore); const otl = buildOtl(para, gs.glyphStore);
// Regulate // Regulate
@ -25,12 +27,13 @@ export async function buildFont(argv, para) {
for (let p = start; p <= end; p++) excludeChars.add(p); for (let p = start; p <= end; p++) excludeChars.add(p);
} }
} }
// Finalize (like geometry conversion) // Finalize (like geometry conversion)
const cache = await Caching.load(argv.iCache, argv.menu.version, argv.cacheFreshAgeKey); const cache = await Caching.load(argv.iCache, argv.menu.version, argv.cacheFreshAgeKey);
const finalGs = finalizeFont(cache, para, gs.glyphStore, excludeChars, otl); const finalGs = finalizeFont(cache, para, gs.glyphStore, excludeChars, otl);
if (cache.isUpdated()) { if (cache.isUpdated()) await Caching.save(argv.oCache, argv.menu.version, cache, true);
await Caching.save(argv.oCache, argv.menu.version, cache, true);
} // Convert to TTF
const font = await convertOtd(baseFont, otl, finalGs); const font = await convertOtd(baseFont, otl, finalGs);
const ttfaControls = await generateTtfaControls(finalGs, font.glyphs); const ttfaControls = await generateTtfaControls(finalGs, font.glyphs);
return { font, glyphStore: finalGs, cacheUpdated: cache.isUpdated(), ttfaControls }; return { font, glyphStore: finalGs, cacheUpdated: cache.isUpdated(), ttfaControls };

View file

@ -253,7 +253,7 @@ glyph-block AutoBuild-Enclosure : begin
define ItalicSpacing : object define ItalicSpacing : object
gniPrefix 'i' gniPrefix 'i'
getPara : function[pp digits rows width] : begin 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.slope 'italic'
set a.shape.slopeAngle : mix (para.slopeAngle || 0) 15 (95 / 150) set a.shape.slopeAngle : mix (para.slopeAngle || 0) 15 (95 / 150)
return : StandardSpacing.getPara pp1 digits rows width return : StandardSpacing.getPara pp1 digits rows width
@ -261,7 +261,7 @@ glyph-block AutoBuild-Enclosure : begin
define SansSerifSpacing : object define SansSerifSpacing : object
gniPrefix 'ss' gniPrefix 'ss'
getPara : function [pp digits rows width] : begin 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' set a.shape.serifs 'sans'
return : StandardSpacing.getPara pp1 digits rows width return : StandardSpacing.getPara pp1 digits rows width

View file

@ -674,7 +674,7 @@ glyph-block Autobuild-Transformed : begin
: where : [createReversed _records] : begin : where : [createReversed _records] : begin
local { records relSets targetNameMap } : extendRelatedGlyphs 'reversed' _records local { records relSets targetNameMap } : extendRelatedGlyphs 'reversed' _records
local pendingGlyphs : records.map : [record] => record.1 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) set p.shape.slopeAngle (-p.shape.slopeAngle)
local miniatureFont : Fork pendingGlyphs forkedPara local miniatureFont : Fork pendingGlyphs forkedPara
@ -702,7 +702,7 @@ glyph-block Autobuild-Transformed-Mathematical : begin
define [createMathDerivedSeriesImpl groupName tfm _records] : begin define [createMathDerivedSeriesImpl groupName tfm _records] : begin
local { records relSets targetNameMap } : extendRelatedGlyphs groupName _records local { records relSets targetNameMap } : extendRelatedGlyphs groupName _records
local pendingGlyphs : records.map : [record] => record.1 local pendingGlyphs : records.map : [record] => record.1
local forkedPara : para.reinit tfm local forkedPara : para.createFork tfm
local forked : Fork pendingGlyphs forkedPara local forked : Fork pendingGlyphs forkedPara
foreach {unicode glyphid} [items-of records] : if [not : query-glyph targetNameMap.(glyphid)] foreach {unicode glyphid} [items-of records] : if [not : query-glyph targetNameMap.(glyphid)]
create-glyph targetNameMap.(glyphid) unicode : glyph-proc create-glyph targetNameMap.(glyphid) unicode : glyph-proc

View file

@ -15,7 +15,7 @@ $$include '../meta/macros.ptl'
export : define [buildGlyphs para recursive] : begin export : define [buildGlyphs para recursive] : begin
# Execution and dependency management # Execution and dependency management
local $Exec$ : new GlyphBuildExecutor local $Exec$ : new GlyphBuildExecutor recursive
define [glyph-is-needed name] : [not recursive] || [recursive.glyphIsNeeded name] define [glyph-is-needed name] : [not recursive] || [recursive.glyphIsNeeded name]
# Initialize glyph store # 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/transformed.mjs"
run-glyph-module "./auto-build/composite.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 'NWID' 'WWID' Gr.Nwid Gr.Wwid
Gr.linkSuffixPairGr glyphStore 'lnum' 'onum' Gr.Lnum Gr.Onum Gr.linkSuffixPairGr glyphStore 'lnum' 'onum' Gr.Lnum Gr.Onum

View file

@ -2,7 +2,7 @@ $$include '../../../meta/macros.ptl'
glyph-module glyph-module
glyph-block Letter-Cyrillic-Orthography : begin glyph-block Letter-Latin-Orthography : begin
glyph-block-import Common-Derivatives glyph-block-import Common-Derivatives
# orthographic-italic 'f_i' 0xFB01 # orthographic-italic 'f_i' 0xFB01

View file

@ -6,7 +6,7 @@ import [bitOr] from"../../../support/util/mask-bit.mjs"
glyph-module 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 CommonShapes
glyph-block-import Common-Derivatives glyph-block-import Common-Derivatives
glyph-block-import Letter-Latin-Upper-F : EFVJutLength glyph-block-import Letter-Latin-Upper-F : EFVJutLength

View file

@ -5,7 +5,7 @@ import [Joining] from"../../support/gr.mjs"
glyph-module glyph-module
glyph-block Symbol-Mosaic-NotDef : begin glyph-block Spaces : begin
glyph-block-import CommonShapes glyph-block-import CommonShapes
glyph-block-import Common-Derivatives glyph-block-import Common-Derivatives

View file

@ -5,7 +5,7 @@ import [DesignParameters] from"../../../meta/aesthetics.mjs"
glyph-module 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 CommonShapes
glyph-block-import Common-Derivatives glyph-block-import Common-Derivatives
glyph-block-import Symbol-Geometric-Shared : GeometricDim UnicodeWeightGrade GeometricSizes glyph-block-import Symbol-Geometric-Shared : GeometricDim UnicodeWeightGrade GeometricSizes

View file

@ -5,7 +5,7 @@ import [mix linreg clamp fallback] from"../../../support/utils.mjs"
glyph-module glyph-module
glyph-block Symbol-Punctuation-ParagraphAndSection : begin glyph-block Symbol-Punctuation-Pilcrow : begin
glyph-block-import CommonShapes glyph-block-import CommonShapes
glyph-block-import Common-Derivatives glyph-block-import Common-Derivatives

View file

@ -5,7 +5,7 @@ import [mix linreg clamp fallback] from"../../../support/utils.mjs"
glyph-module glyph-module
glyph-block Symbol-Punctuation-ParagraphAndSection : begin glyph-block Symbol-Punctuation-Section : begin
glyph-block-import CommonShapes glyph-block-import CommonShapes
glyph-block-import Common-Derivatives glyph-block-import Common-Derivatives

View file

@ -70,16 +70,16 @@ async function getParameters() {
}; };
return para; return para;
} }
function reinit(argv) { function paraT(argv) {
const para = createParaImpl(argv); const para = createParaImpl(argv);
para.reinit = function (tf) { para.createFork = function (tf) {
const argv1 = deepClone(argv); const argv1 = deepClone(argv);
tf(argv1, argv); tf(argv1, argv);
return reinit(argv1); return paraT(argv1);
}; };
return para; return para;
} }
return reinit; return paraT;
} }
async function tryParseToml(str) { async function tryParseToml(str) {
try { try {

View file

@ -92,11 +92,11 @@ export : define [buildCVSS gsub para glyphStore] : begin
if (variant.tag && variant.rank) : cvGrs.push variant if (variant.tag && variant.rank) : cvGrs.push variant
cvGrs.sort AnyCv.compare cvGrs.sort AnyCv.compare
foreach gr [items-of cvGrs] : begin foreach {gn glyph} [glyphStore.namedEntries] : if [not : CvDecompose.get glyph] : begin
local cvAlt : [cvs.get gr.tag].createAlternateSubst foreach gr [items-of cvGrs] : begin
foreach {gn glyph} [glyphStore.namedEntries] : if [not : CvDecompose.get glyph] : begin
local subst : gr.get glyph local subst : gr.get glyph
if (subst && subst != gn) : begin if (subst && subst != gn) : begin
local cvAlt : [cvs.get gr.tag].createAlternateSubst
if [not cvAlt.substitutions.(gn)] : set cvAlt.substitutions.(gn) { } if [not cvAlt.substitutions.(gn)] : set cvAlt.substitutions.(gn) { }
set cvAlt.substitutions.(gn).(gr.rank - 1) : glyphStore.ensureExists subst 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 foreach gr [items-of ssGrs] : begin
local cvSingle : [cvs.get gr.tag].createSingleSubstFor gr.rank local cvSingle : [cvs.get gr.tag].createSingleSubstFor gr.rank
feature.addLookup cvSingle 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 local subst : gr.get glyph
if (subst && subst != gn) : begin if (subst && subst != gn) : begin
local cvSingle : [cvs.get gr.tag].createSingleSubstFor gr.rank
set cvSingle.substitutions.(gn) : glyphStore.ensureExists subst set cvSingle.substitutions.(gn) : glyphStore.ensureExists subst
do "Cleanup and link dependency" do "Cleanup and link dependency"

View file

@ -1,35 +1,57 @@
export class GlyphBuildExecutor { export class GlyphBuildExecutor {
constructor() { constructor(recursiveBuildFilter) {
this.currentBlockName = null; this.recursiveBuildFilter = recursiveBuildFilter;
this.currentBlockId = null;
this.dependencyManager = new DependencyManager(); this.dependencyManager = new DependencyManager();
this.pendingGlyphBlocks = []; this.pendingGlyphBlocks = [];
this.glyphBlockStore = {}; this.glyphBlockStore = {};
} }
setGlyphToBlockDependency(glyph) { setGlyphToBlockDependency(glyph) {
if (this.currentBlockName) { if (this.currentBlockId) {
this.dependencyManager.glyphToBlock.set(glyph, this.currentBlockName); 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) { executePendingBlocks() {
const block = new GlyphBlock(capture, this, blockName, body); // if (!this.recursiveBuildFilter) {
this.glyphBlockStore[blockName] = block; 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); this.pendingGlyphBlocks.push(block);
} }
} }
export class RecursiveBuildFilter { export class RecursiveBuildFilter {
constructor(glyphIdFilter) { constructor(glyphIdFilter, blockIdFilter) {
this.glyphIdFilter = glyphIdFilter; this.glyphIdFilter = glyphIdFilter;
this.blockIdFilter = blockIdFilter;
} }
glyphIsNeeded(id) { glyphIsNeeded(id) {
return this.glyphIdFilter.has(id); return this.glyphIdFilter.has(id);
} }
blockIsNeeded(id) {
return this.blockIdFilter.has(id);
}
} }
export class DependencyManager { export class DependencyManager {
constructor() { constructor() {
this.glyphToGlyph = new WeakMap(); this.glyphToGlyph = new WeakMap();
this.glyphToBlock = new WeakMap(); this.glyphToBlock = new WeakMap();
this.blockToGlyph = new Map();
} }
addDependency(dependent, dependency) { addDependency(dependent, dependency) {
let s = this.glyphToGlyph.get(dependent); let s = this.glyphToGlyph.get(dependent);
@ -39,12 +61,31 @@ export class DependencyManager {
} }
s.add(dependency); s.add(dependency);
} }
traverseDependencies(glyphs) {
traverseGlyphDependenciesImpl(glyphs, fBlockwiseExpand) {
let state = new Map(); let state = new Map();
const PENDING = 1, const PENDING = 1,
CHECKED = 2; CHECKED = 2;
for (const glyph of glyphs) state.set(glyph, PENDING); 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 (;;) { for (;;) {
let found = false; let found = false;
for (const [glyph, s] of state) { for (const [glyph, s] of state) {
@ -59,11 +100,24 @@ export class DependencyManager {
if (!found) break; if (!found) break;
} }
return state;
}
traverseDependencies(glyphs) {
const gGlyphGraph = this.traverseGlyphDependenciesImpl(glyphs, false);
const gBlockGraph = this.traverseGlyphDependenciesImpl(glyphs, true);
let glyphIdFilter = new Set(); 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); 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) { constructor(capture, execState, blockName, body) {
this.capture = capture; this.capture = capture;
this.execState = execState; this.execState = execState;
this.blockName = blockName; this.id = blockName;
this.body = body; this.body = body;
this.resolved = 0; this.resolved = 0;
this.exports = {}; this.exports = {};
} }
resolve() { resolve() {
if (this.resolved == 2) return this.exports; 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; this.resolved = 1;
const prevBlockName = this.execState.currentBlockName; const prevBlockName = this.execState.currentBlockId;
this.execState.currentBlockName = this.blockName; this.execState.currentBlockId = this.id;
const pendingApplications = []; const pendingApplications = [];
const ExportCapture = fnObj => { const ExportCapture = fnObj => {
@ -95,7 +149,7 @@ export class GlyphBlock {
this.body(this.capture, ExportCapture); this.body(this.capture, ExportCapture);
for (const f of pendingApplications) f(); for (const f of pendingApplications) f();
this.execState.currentBlockName = prevBlockName; this.execState.currentBlockId = prevBlockName;
this.resolved = 2; this.resolved = 2;
return this.exports; return this.exports;
} }

View file

@ -112,6 +112,7 @@ async function processSsStyles() {
if (!gr.rank) continue; if (!gr.rank) continue;
md.log(` - \`${gr.tag}\`: Set character variant to “${gr.description}”.`); md.log(` - \`${gr.tag}\`: Set character variant to “${gr.description}”.`);
} }
md.log(` - Other build plans configuration, using \`inherits = "buildPlans.<Plan name>"\`.`);
return md; return md;
} }

View file

@ -31,7 +31,7 @@ const DIST_SUPER_TTC = "dist/.super-ttc";
const ARCHIVE_DIR = "release-archives"; const ARCHIVE_DIR = "release-archives";
const PATEL_C = ["node", "node_modules/patel/bin/patel-c"]; 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 SEVEN_ZIP = process.env.SEVEN_ZIP_PATH || "7z";
const TTFAUTOHINT = process.env.TTFAUTOHINT_PATH || "ttfautohint"; 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 [rp] = await target.need(RawPlans);
const rawBuildPlans = rp.buildPlans; const rawBuildPlans = rp.buildPlans;
// Initialize build plans
const returnBuildPlans = {}; const returnBuildPlans = {};
const fileNameToBpMap = {};
for (const prefix in rawBuildPlans) { for (const prefix in rawBuildPlans) {
const bp = { ...rawBuildPlans[prefix] }; 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.webfontFormats = bp["webfont-formats"] || defaultWebFontFormats;
bp.targets = []; 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, const weights = bp.weights,
slopes = bp.slopes, slopes = bp.slopes,
widths = bp.widths; widths = bp.widths;
@ -157,31 +169,31 @@ const BuildPlans = computed("metadata:build-plans", async target => {
function linkSpacingDerivableBuildPlans(bps) { function linkSpacingDerivableBuildPlans(bps) {
for (const pfxTo in bps) { for (const pfxTo in bps) {
const planTo = bps[pfxTo]; const bpTo = bps[pfxTo];
const planToVal = rectifyPlanForSpacingDerivation(planTo); if (blockSpacingDerivation(bpTo)) continue;
if (blockSpacingDerivation(planTo)) continue; if (!isDeriveToSpacing(bpTo.spacing)) continue;
if (!isLinkDeriveToSpacing(planTo.spacing)) continue;
for (const pfxFrom in bps) { for (const pfxFrom in bps) {
const planFrom = bps[pfxFrom]; const bpFrom = bps[pfxFrom];
if (!isLinkDeriveFromSpacing(planFrom.spacing)) continue; if (!isDeriveFromSpacing(bpFrom.spacing)) continue;
const planFromVal = rectifyPlanForSpacingDerivation(planFrom); if (!spacingDeriveCompatible(pfxTo, bpTo, pfxFrom, bpFrom)) continue;
if (!deepEqual(planToVal, planFromVal)) continue; bpTo.spacingDeriveFrom = pfxFrom;
planTo.spacingDeriveFrom = pfxFrom;
} }
} }
} }
function blockSpacingDerivation(bp) { function blockSpacingDerivation(bp) {
return !!bp["compatibility-ligatures"]; return !!bp["compatibility-ligatures"];
} }
function isLinkDeriveToSpacing(spacing) { function isDeriveToSpacing(spacing) {
return spacing === "term" || spacing === "fontconfig-mono" || spacing === "fixed"; return spacing === "term" || spacing === "fontconfig-mono" || spacing === "fixed";
} }
function isLinkDeriveFromSpacing(spacing) { function isDeriveFromSpacing(spacing) {
return !spacing || spacing === "normal"; return !spacing || spacing === "normal";
} }
function spacingDeriveCompatible(pfxTo, bpTo, pfxFrom, bpFrom) {
function rectifyPlanForSpacingDerivation(p) { // If the two build plans are the same, then they are compatible.
return deepEqual(rectifyPlanForSpacingDerive(bpTo), rectifyPlanForSpacingDerive(bpFrom));
}
function rectifyPlanForSpacingDerive(p) {
return { return {
...p, ...p,
family: "#Validation", family: "#Validation",
@ -749,7 +761,7 @@ const GlyfTtc = file.make(
async function buildCompositeTtc(out, inputs) { async function buildCompositeTtc(out, inputs) {
const inputPaths = inputs.map(f => f.full); const inputPaths = inputs.map(f => f.full);
echo.action(echo.hl.command(`Create TTC`), out.full, echo.hl.operator("<-"), inputPaths); 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) { 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 [ttfInputs] = await target.need(parts.map(part => BuildNoGcTtf(part.dir, part.file)));
const ttfInputPaths = ttfInputs.map(p => p.full); const ttfInputPaths = ttfInputs.map(p => p.full);
echo.action(echo.hl.command(`Create TTC`), out.full, echo.hl.operator("<-"), ttfInputPaths); 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 // Build plan validation
function validateAndShimBuildPlans(prefix, bp, dWeights, dSlopes, dWidths) {
if (!bp.family) { function resolveWws(bpName, buildPlans, defaultConfig) {
fail(`Build plan for ${prefix} does not have a family name. Exit.`); const bp = buildPlans[bpName];
} if (!bp) fail(`Build plan ${bpName} not found.`);
if (!bp.slopes && bp.slants) { if (!bp.slopes && bp.slants) {
echo.warn( fail(
`Build plan for ${prefix} uses legacy "slants" to define slopes. ` + `Build plan for ${bpName} uses legacy "slants" to define slopes. ` +
`Use "slopes" instead.` `Use "slopes" instead.`
); );
} }
bp.weights = shimBpAspect("weights", bp.weights, dWeights); bp.weights = resolveWwsAspect("weights", bpName, buildPlans, defaultConfig, []);
bp.slopes = shimBpAspect("slopes", bp.slopes || bp.slants, dSlopes); bp.widths = resolveWwsAspect("widths", bpName, buildPlans, defaultConfig, []);
bp.widths = shimBpAspect("widths", bp.widths, dWidths); 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) { function shimBpAspect(aspectName, aspect, defaultAspect) {
if (!aspect) return defaultAspect; if (!aspect) return defaultAspect;
const result = {}; const result = {};