/* * Jakefile * Tests/Manual * * Created by David Richardson, September 8, 2026 * Copyright 2026, David Richardson. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ var ENV = process.env, task = JAKE.task, cp = require("child_process"), fs = require("fs"), path = require("path"); // The frameworks an application's Frameworks directory links to, in the layout // capp gen creates: release builds directly inside, debug builds in Debug/. var ConfiguredFrameworks = ["Objective-J", "Foundation", "AppKit", "BlendKit"]; /* * Standard build targets are neutered at this directory level to prevent automated * CI runners from triggering exhaustive, recursive compilations across all manual test suites. */ task ("default", function() { console.log("Global traversal disabled to prevent CI timeouts."); console.log("Use 'npx jake batch-compile' or 'npx jake batch-compile-all' to deliberately build the suite."); }); task ("build", ["default"]); task ("build-all", ["default"]); task ("debug", ["default"]); task ("release", ["default"]); /* * ----------------------------------------------------------------------------- * Environment Management * ----------------------------------------------------------------------------- */ /* * Gives every manual test application the Frameworks layout capp gen creates: * Frameworks/ links to the release build in Build/Release, and * Frameworks/Debug links to the debug build in Build/Debug, so index.html loads * release frameworks and index-debug.html loads debug frameworks. An existing * Frameworks link, or a Frameworks directory holding only links, is replaced; * a Frameworks directory holding real files is left untouched. */ task ("configure", function() { var manualDir = __dirname, releasePath = path.resolve(manualDir, "../../Build/Release"), debugPath = path.resolve(manualDir, "../../Build/Debug"); [releasePath, debugPath].forEach(function(buildPath) { ConfiguredFrameworks.forEach(function(framework) { if (!fs.existsSync(path.join(buildPath, framework))) throw new Error(path.join(buildPath, framework) + " not found; build the frameworks in both configurations first"); }); }); manualApplicationDirectories(manualDir).forEach(function(appDir) { var name = path.basename(appDir), frameworksPath = path.join(appDir, "Frameworks"); if (!removeConfiguredFrameworks(appDir)) { console.log("Skipping " + name + ": Frameworks contains files that are not links"); return; } console.log("Configuring " + name); fs.mkdirSync(frameworksPath); ConfiguredFrameworks.forEach(function(framework) { fs.symlinkSync(path.join(releasePath, framework), path.join(frameworksPath, framework)); }); fs.symlinkSync(debugPath, path.join(frameworksPath, "Debug")); }); }); /* * Cleans the subsidiary applications' Build directories exclusively (-mindepth 2 -maxdepth 2). */ task ("clean", function() { var manualDir = __dirname; var targetName = path.basename(ENV["BUILD_PATH"] || ENV["CAPP_BUILD"] || "Build"); cp.execSync("find . -mindepth 2 -maxdepth 2 -type d -name '" + targetName + "' -exec rm -rf {} +", { stdio: "inherit", cwd: manualDir }); }); /* * Removes the Frameworks layout configure created from every manual test * application, after delegating Build directory removal to clean. A Frameworks * directory holding real files is left untouched. */ task ("clobber", ["clean"], function() { var manualDir = __dirname; manualApplicationDirectories(manualDir).forEach(function(appDir) { if (!removeConfiguredFrameworks(appDir)) console.log("Skipping " + path.basename(appDir) + ": Frameworks contains files that are not links"); }); }); /* * Returns the absolute paths of the manual test applications: every directory * below this one that contains a Jakefile. */ function manualApplicationDirectories(manualDir) { var jakefiles = cp.execSync("find . -mindepth 2 -iname 'jakefile'", { cwd: manualDir, encoding: "utf8" }); return jakefiles.split("\n").filter(Boolean).map(function(jakefile) { return path.resolve(manualDir, path.dirname(jakefile)); }); } /* * Removes an application's Frameworks link, or its Frameworks directory when * that holds only links and directories, and returns true; returns false and * leaves it in place when it holds any real file. Links are removed, never * followed. */ function removeConfiguredFrameworks(appDir) { var frameworksPath = path.join(appDir, "Frameworks"), stat; try { stat = fs.lstatSync(frameworksPath); } catch (anError) { return true; } if (stat.isSymbolicLink()) { fs.unlinkSync(frameworksPath); return true; } if (stat.isDirectory() && containsOnlyLinks(frameworksPath)) { fs.rmSync(frameworksPath, { recursive: true }); return true; } return false; } /* * Returns true when aPath is a directory tree holding only symbolic links and * directories. */ function containsOnlyLinks(aPath) { return fs.readdirSync(aPath).every(function(name) { var entry = path.join(aPath, name), stat = fs.lstatSync(entry); if (stat.isSymbolicLink()) return true; if (stat.isDirectory()) return containsOnlyLinks(entry); return false; }); } /* * ----------------------------------------------------------------------------- * Manual Execution Targets * ----------------------------------------------------------------------------- */ /* * Triggers baseline compilation across all subproject manual tests for local testing. */ task ("batch-compile", function() { var manualDir = __dirname; cp.execSync("find . -mindepth 2 -iname 'jakefile' -execdir npx jake build \\;", { stdio: "inherit", env: ENV, cwd: manualDir }); }); task ("batch-compile-all", ["batch-compile-debug", "batch-compile-release"]); task ("batch-compile-debug", function() { var manualDir = __dirname; ENV["CONFIGURATION"] = "Debug"; cp.execSync("find . -mindepth 2 -iname 'jakefile' -execdir npx jake debug \\;", { stdio: "inherit", env: ENV, cwd: manualDir }); }); task ("batch-compile-release", function() { var manualDir = __dirname; ENV["CONFIGURATION"] = "Release"; cp.execSync("find . -mindepth 2 -iname 'jakefile' -execdir npx jake release \\;", { stdio: "inherit", env: ENV, cwd: manualDir }); });