mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-21 09:46:02 +08:00
299 lines
10 KiB
TypeScript
Executable File
299 lines
10 KiB
TypeScript
Executable File
#!/usr/bin/env npx tsx
|
|
|
|
import { execSync } from 'child_process';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import { promisify } from 'util';
|
|
|
|
const writeFile = promisify(fs.writeFile);
|
|
const mkdir = promisify(fs.mkdir);
|
|
const stat = promisify(fs.stat);
|
|
|
|
// Helper function to get modification time of a file
|
|
async function getMTime(filePath: string): Promise<number> {
|
|
try {
|
|
const stats = await stat(filePath);
|
|
return stats.mtimeMs;
|
|
} catch {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Helper function to find newest file in a directory pattern
|
|
async function getNewestFileTime(baseDir: string, patterns: string[]): Promise<number> {
|
|
let newest = 0;
|
|
|
|
for (const pattern of patterns) {
|
|
const globPattern = path.join(baseDir, pattern);
|
|
const files = execSync(`find "${baseDir}" -name "${pattern.split('/').pop()}" -type f 2>/dev/null || true`, {
|
|
encoding: 'utf8'
|
|
}).trim().split('\n').filter(f => f);
|
|
|
|
for (const file of files) {
|
|
const mtime = await getMTime(file);
|
|
if (mtime > newest) newest = mtime;
|
|
}
|
|
}
|
|
|
|
return newest;
|
|
}
|
|
|
|
// Parse command line arguments
|
|
const args = process.argv.slice(2);
|
|
if (args.length < 2) {
|
|
console.error('Usage: compare-with-reference-impl.ts <skeleton-path> <atlas-path> [animation-name]');
|
|
process.exit(1);
|
|
}
|
|
|
|
const [skeletonPath, atlasPath, animationName] = args;
|
|
|
|
// Get absolute paths
|
|
const absoluteSkeletonPath = path.resolve(skeletonPath);
|
|
const absoluteAtlasPath = path.resolve(atlasPath);
|
|
|
|
// Script paths
|
|
const scriptDir = path.dirname(new URL(import.meta.url).pathname);
|
|
const rootDir = path.dirname(scriptDir);
|
|
const outputDir = path.join(scriptDir, 'output');
|
|
|
|
interface RuntimeConfig {
|
|
name: string;
|
|
buildCheck: () => Promise<boolean>;
|
|
build: () => void;
|
|
run: () => string;
|
|
}
|
|
|
|
// Runtime configurations
|
|
const runtimes: RuntimeConfig[] = [
|
|
{
|
|
name: 'java',
|
|
buildCheck: async () => {
|
|
const classPath = path.join(rootDir, 'spine-libgdx/spine-libgdx-tests/build/classes/java/main/com/esotericsoftware/spine/DebugPrinter.class');
|
|
if (!fs.existsSync(classPath)) return false;
|
|
|
|
// Check if any source files are newer than the class file
|
|
const classTime = await getMTime(classPath);
|
|
const sourceTime = await getNewestFileTime(
|
|
path.join(rootDir, 'spine-libgdx'),
|
|
['spine-libgdx/src/**/*.java', 'spine-libgdx-tests/src/**/*.java']
|
|
);
|
|
|
|
return sourceTime <= classTime;
|
|
},
|
|
build: () => {
|
|
console.log(' Building Java runtime...');
|
|
execSync('./gradlew :spine-libgdx-tests:build', {
|
|
cwd: path.join(rootDir, 'spine-libgdx'),
|
|
stdio: 'inherit'
|
|
});
|
|
},
|
|
run: () => {
|
|
const args = animationName
|
|
? `${absoluteSkeletonPath} ${absoluteAtlasPath} ${animationName}`
|
|
: `${absoluteSkeletonPath} ${absoluteAtlasPath}`;
|
|
const output = execSync(
|
|
`./gradlew -q :spine-libgdx-tests:runDebugPrinter -Pargs="${args}"`,
|
|
{
|
|
cwd: path.join(rootDir, 'spine-libgdx'),
|
|
encoding: 'utf8'
|
|
}
|
|
);
|
|
// Find the start of actual output and return everything from there
|
|
const lines = output.split('\n');
|
|
const startIndex = lines.findIndex(line => line === '=== SKELETON DATA ===');
|
|
if (startIndex !== -1) {
|
|
return lines.slice(startIndex).join('\n').trim();
|
|
}
|
|
// Fallback to full output if marker not found
|
|
return output.trim();
|
|
}
|
|
},
|
|
{
|
|
name: 'cpp',
|
|
buildCheck: async () => {
|
|
const execPath = path.join(rootDir, 'spine-cpp/build/debug-printer');
|
|
if (!fs.existsSync(execPath)) return false;
|
|
|
|
// Check if any source files are newer than the executable
|
|
const execTime = await getMTime(execPath);
|
|
const sourceTime = await getNewestFileTime(
|
|
path.join(rootDir, 'spine-cpp'),
|
|
['spine-cpp/src/**/*.cpp', 'spine-cpp/include/**/*.h', 'tests/DebugPrinter.cpp']
|
|
);
|
|
|
|
return sourceTime <= execTime;
|
|
},
|
|
build: () => {
|
|
console.log(' Building C++ runtime...');
|
|
execSync('./build.sh clean', {
|
|
cwd: path.join(rootDir, 'spine-cpp/'),
|
|
stdio: 'inherit'
|
|
});
|
|
},
|
|
run: () => {
|
|
return execSync(
|
|
animationName
|
|
? `./build/debug-printer "${absoluteSkeletonPath}" "${absoluteAtlasPath}" "${animationName}"`
|
|
: `./build/debug-printer "${absoluteSkeletonPath}" "${absoluteAtlasPath}"`,
|
|
{
|
|
cwd: path.join(rootDir, 'spine-cpp'),
|
|
encoding: 'utf8'
|
|
}
|
|
).trim();
|
|
}
|
|
},
|
|
{
|
|
name: 'c',
|
|
buildCheck: async () => {
|
|
const execPath = path.join(rootDir, 'spine-c/build/debug-printer');
|
|
if (!fs.existsSync(execPath)) return false;
|
|
|
|
// Check if any source files are newer than the executable
|
|
const execTime = await getMTime(execPath);
|
|
const sourceTime = await getNewestFileTime(
|
|
path.join(rootDir, 'spine-c'),
|
|
['src/**/*.c', 'include/**/*.h', 'tests/debug-printer.c']
|
|
);
|
|
|
|
return sourceTime <= execTime;
|
|
},
|
|
build: () => {
|
|
console.log(' Building C runtime...');
|
|
execSync('./build.sh', {
|
|
cwd: path.join(rootDir, 'spine-c/'),
|
|
stdio: 'inherit'
|
|
});
|
|
},
|
|
run: () => {
|
|
return execSync(
|
|
animationName
|
|
? `./build/debug-printer "${absoluteSkeletonPath}" "${absoluteAtlasPath}" "${animationName}"`
|
|
: `./build/debug-printer "${absoluteSkeletonPath}" "${absoluteAtlasPath}"`,
|
|
{
|
|
cwd: path.join(rootDir, 'spine-c'),
|
|
encoding: 'utf8'
|
|
}
|
|
).trim();
|
|
}
|
|
},
|
|
{
|
|
name: 'ts',
|
|
buildCheck: async () => {
|
|
// For TypeScript, just check if the DebugPrinter.ts file exists
|
|
const debugPrinterPath = path.join(rootDir, 'spine-ts/spine-core/tests/DebugPrinter.ts');
|
|
return fs.existsSync(debugPrinterPath);
|
|
},
|
|
build: () => {}, // No build needed
|
|
run: () => {
|
|
return execSync(
|
|
animationName
|
|
? `npx tsx tests/DebugPrinter.ts "${absoluteSkeletonPath}" "${absoluteAtlasPath}" "${animationName}"`
|
|
: `npx tsx tests/DebugPrinter.ts "${absoluteSkeletonPath}" "${absoluteAtlasPath}"`,
|
|
{
|
|
cwd: path.join(rootDir, 'spine-ts/spine-core'),
|
|
encoding: 'utf8'
|
|
}
|
|
).trim();
|
|
}
|
|
}
|
|
];
|
|
|
|
async function main() {
|
|
// Ensure output directory exists
|
|
await mkdir(outputDir, { recursive: true });
|
|
|
|
console.log('Comparing DebugPrinter outputs for:');
|
|
console.log(` Skeleton: ${absoluteSkeletonPath}`);
|
|
console.log(` Atlas: ${absoluteAtlasPath}`);
|
|
console.log(` Animation: ${animationName}`);
|
|
console.log('');
|
|
|
|
// Run all runtimes and collect outputs
|
|
const outputs: Record<string, string> = {};
|
|
|
|
for (const runtime of runtimes) {
|
|
console.log(`Running ${runtime.name.toUpperCase()} DebugPrinter...`);
|
|
|
|
try {
|
|
// Build if needed
|
|
if (!(await runtime.buildCheck())) {
|
|
runtime.build();
|
|
}
|
|
|
|
// Run and capture output
|
|
const output = runtime.run();
|
|
outputs[runtime.name] = output;
|
|
|
|
// Save output to file
|
|
await writeFile(path.join(outputDir, `${runtime.name}.txt`), output);
|
|
|
|
console.log(' Done.');
|
|
} catch (error) {
|
|
console.error(` Error: ${error instanceof Error ? error.message : JSON.stringify(error)}`);
|
|
outputs[runtime.name] = `Error: ${error instanceof Error ? error.message : JSON.stringify(error)}`;
|
|
}
|
|
}
|
|
|
|
console.log('');
|
|
console.log('Comparing outputs...');
|
|
console.log('');
|
|
|
|
// Compare outputs
|
|
const reference = 'java';
|
|
let allMatch = true;
|
|
|
|
for (const runtime of runtimes) {
|
|
if (runtime.name !== reference) {
|
|
process.stdout.write(`Comparing ${reference} vs ${runtime.name}: `);
|
|
|
|
if (outputs[reference] === outputs[runtime.name]) {
|
|
console.log('✓ MATCH');
|
|
} else {
|
|
console.log('✗ DIFFER');
|
|
allMatch = false;
|
|
|
|
// Show first few differences
|
|
const refLines = outputs[reference].split('\n');
|
|
const runtimeLines = outputs[runtime.name].split('\n');
|
|
const maxLines = Math.max(refLines.length, runtimeLines.length);
|
|
let diffCount = 0;
|
|
|
|
console.log(' First differences:');
|
|
for (let i = 0; i < maxLines && diffCount < 5; i++) {
|
|
if (refLines[i] !== runtimeLines[i]) {
|
|
diffCount++;
|
|
console.log(` Line ${i + 1}:`);
|
|
if (refLines[i] !== undefined) {
|
|
console.log(` - ${refLines[i]}`);
|
|
}
|
|
if (runtimeLines[i] !== undefined) {
|
|
console.log(` + ${runtimeLines[i]}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (diffCount === 0 && refLines.length !== runtimeLines.length) {
|
|
console.log(` Different number of lines: ${refLines.length} vs ${runtimeLines.length}`);
|
|
}
|
|
|
|
// Save outputs for manual diff
|
|
console.log(` Full outputs saved to: ${outputDir}/`);
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log('');
|
|
if (allMatch) {
|
|
console.log('✓ All outputs match!');
|
|
process.exit(0);
|
|
} else {
|
|
console.log(`✗ Outputs differ. Check the output files in ${outputDir}/`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Run main function
|
|
main().catch(error => {
|
|
console.error('Error:', error);
|
|
process.exit(1);
|
|
}); |