forked from LeenkxTeam/LNXSDK
Update Files
This commit is contained in:
7
Kha/Tools/khamake/src/Architecture.ts
Normal file
7
Kha/Tools/khamake/src/Architecture.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export let Architecture = {
|
||||
Default: 'default',
|
||||
Arm7: 'arm7',
|
||||
Arm8: 'arm8',
|
||||
X86: 'x86',
|
||||
X86_64: 'x86_64'
|
||||
};
|
282
Kha/Tools/khamake/src/AssetConverter.ts
Normal file
282
Kha/Tools/khamake/src/AssetConverter.ts
Normal file
@ -0,0 +1,282 @@
|
||||
import {Callbacks} from './ProjectFile';
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import {KhaExporter} from './Exporters/KhaExporter';
|
||||
import * as log from './log';
|
||||
import * as chokidar from 'chokidar';
|
||||
import * as crypto from 'crypto';
|
||||
import * as Throttle from 'promise-parallel-throttle';
|
||||
import { Options } from './Options';
|
||||
|
||||
export class AssetConverter {
|
||||
options: Options;
|
||||
exporter: KhaExporter;
|
||||
platform: string;
|
||||
assetMatchers: Array<{ match: string, options: any }>;
|
||||
watcher: fs.FSWatcher;
|
||||
|
||||
constructor(exporter: KhaExporter, options: Options, assetMatchers: Array<{ match: string, options: any }>) {
|
||||
this.exporter = exporter;
|
||||
this.options = options;
|
||||
this.platform = options.target;
|
||||
this.assetMatchers = assetMatchers;
|
||||
}
|
||||
|
||||
close(): void {
|
||||
if (this.watcher) this.watcher.close();
|
||||
}
|
||||
|
||||
static replacePattern(pattern: string, name: string, fileinfo: path.ParsedPath, options: any, from: string) {
|
||||
let basePath: string = options.nameBaseDir ? path.join(from, options.nameBaseDir) : from;
|
||||
let dirValue: string = path.relative(basePath, fileinfo.dir);
|
||||
if (basePath.length > 0 && basePath[basePath.length - 1] === path.sep
|
||||
&& dirValue.length > 0 && dirValue[dirValue.length - 1] !== path.sep) {
|
||||
dirValue += path.sep;
|
||||
}
|
||||
if (options.namePathSeparator) {
|
||||
dirValue = dirValue.split(path.sep).join(options.namePathSeparator);
|
||||
}
|
||||
|
||||
const dirRegex = dirValue === ''
|
||||
? /{dir}\//g
|
||||
: /{dir}/g;
|
||||
|
||||
return pattern.replace(/{name}/g, name).replace(/{ext}/g, fileinfo.ext).replace(dirRegex, dirValue);
|
||||
}
|
||||
|
||||
static createExportInfo(fileinfo: path.ParsedPath, keepextension: boolean, options: any, from: string): {name: string, destination: string} {
|
||||
let nameValue = fileinfo.name;
|
||||
|
||||
let destination = fileinfo.name;
|
||||
|
||||
if (options.md5sum) {
|
||||
let data = fs.readFileSync(path.join(fileinfo.dir, fileinfo.base));
|
||||
let md5sum = crypto.createHash('md5').update(data).digest('hex'); // TODO yield generateMd5Sum(file);
|
||||
destination += '_' + md5sum;
|
||||
}
|
||||
if ((keepextension || options.noprocessing) && (!options.destination || options.destination.indexOf('{ext}') < 0)) {
|
||||
destination += fileinfo.ext;
|
||||
}
|
||||
|
||||
if (options.destination) {
|
||||
destination = AssetConverter.replacePattern(options.destination, destination, fileinfo, options, from);
|
||||
}
|
||||
|
||||
if (options.destinationCallback) {
|
||||
destination = options.destinationCallback(destination);
|
||||
}
|
||||
|
||||
if (keepextension && (!options.name || options.name.indexOf('{ext}') < 0)) {
|
||||
nameValue += fileinfo.ext;
|
||||
}
|
||||
|
||||
if (options.name) {
|
||||
nameValue = AssetConverter.replacePattern(options.name, nameValue, fileinfo, options, from);
|
||||
}
|
||||
|
||||
return {name: nameValue, destination: destination};
|
||||
}
|
||||
|
||||
watch(watch: boolean, match: string, temp: string, options: any): Promise<{ name: string, from: string, type: string, files: string[], file_sizes: number[], original_width: number, original_height: number, readable: boolean }[]> {
|
||||
return new Promise<{ name: string, from: string, type: string, files: string[], file_sizes: number[], original_width: number, original_height: number, readable: boolean }[]>((resolve, reject) => {
|
||||
let ready = false;
|
||||
let files: string[] = [];
|
||||
this.watcher = chokidar.watch(match, { ignored: /[\/\\]\.(svn|git|DS_Store)/, persistent: watch, followSymlinks: false });
|
||||
|
||||
const onFileChange = async (file: string) => {
|
||||
const fileinfo = path.parse(file);
|
||||
let outPath = fileinfo.name;
|
||||
// with subfolders
|
||||
if (options.destination) {
|
||||
const from = path.resolve(options.baseDir, '..');
|
||||
outPath = AssetConverter.replacePattern(options.destination, fileinfo.name, fileinfo, options, from);
|
||||
}
|
||||
log.info('Reexporting ' + outPath + fileinfo.ext);
|
||||
switch (fileinfo.ext) {
|
||||
case '.png':
|
||||
case '.jpg':
|
||||
case '.jpeg':
|
||||
case '.hdr': {}
|
||||
await this.exporter.copyImage(this.platform, file, outPath, {}, {});
|
||||
break;
|
||||
|
||||
case '.ogg':
|
||||
case '.mp3':
|
||||
case '.flac':
|
||||
case '.wav': {
|
||||
await this.exporter.copySound(this.platform, file, outPath, {});
|
||||
break;
|
||||
}
|
||||
|
||||
case '.mp4':
|
||||
case '.webm':
|
||||
case '.mov':
|
||||
case '.wmv':
|
||||
case '.avi': {
|
||||
await this.exporter.copyVideo(this.platform, file, outPath, {});
|
||||
break;
|
||||
}
|
||||
|
||||
case '.ttf':
|
||||
await this.exporter.copyFont(this.platform, file, outPath, {});
|
||||
break;
|
||||
|
||||
default:
|
||||
await this.exporter.copyBlob(this.platform, file, outPath + fileinfo.ext, {});
|
||||
}
|
||||
for (let callback of Callbacks.postAssetReexporting) {
|
||||
callback(outPath + fileinfo.ext);
|
||||
}
|
||||
};
|
||||
|
||||
this.watcher.on('add', (file: string) => {
|
||||
if (ready) {
|
||||
onFileChange(file);
|
||||
}
|
||||
else {
|
||||
files.push(file);
|
||||
}
|
||||
});
|
||||
if (watch) {
|
||||
this.watcher.on('change', (file: string) => {
|
||||
if (ready) {
|
||||
onFileChange(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.watcher.on('ready', async () => {
|
||||
ready = true;
|
||||
let parsedFiles: { name: string, from: string, type: string, files: string[], file_sizes: number[], original_width: number, original_height: number, readable: boolean }[] = [];
|
||||
let cache: any = {};
|
||||
let cachePath = path.join(temp, 'cache.json');
|
||||
if (fs.existsSync(cachePath)) {
|
||||
cache = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
|
||||
}
|
||||
|
||||
const self = this;
|
||||
|
||||
async function convertAsset( file: string, index: number ) {
|
||||
let fileinfo = path.parse(file);
|
||||
log.info('Exporting asset ' + (index + 1) + ' of ' + files.length + ' (' + fileinfo.base + ').');
|
||||
const ext = fileinfo.ext.toLowerCase();
|
||||
switch (ext) {
|
||||
case '.png':
|
||||
case '.jpg':
|
||||
case '.jpeg':
|
||||
case '.hdr': {
|
||||
let exportInfo = AssetConverter.createExportInfo(fileinfo, false, options, self.exporter.options.from);
|
||||
let images: { files: string[], sizes: number[] };
|
||||
if (options.noprocessing) {
|
||||
images = await self.exporter.copyBlob(self.platform, file, exportInfo.destination, options);
|
||||
}
|
||||
else {
|
||||
images = await self.exporter.copyImage(self.platform, file, exportInfo.destination, options, cache);
|
||||
}
|
||||
if (!options.notinlist) {
|
||||
parsedFiles.push({ name: exportInfo.name, from: file, type: 'image', files: images.files, file_sizes: images.sizes, original_width: options.original_width, original_height: options.original_height, readable: options.readable });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '.ogg':
|
||||
case '.mp3':
|
||||
case '.flac':
|
||||
case '.wav': {
|
||||
let exportInfo = AssetConverter.createExportInfo(fileinfo, false, options, self.exporter.options.from);
|
||||
let sounds: { files: string[], sizes: number[] };
|
||||
if (options.noprocessing) {
|
||||
sounds = await self.exporter.copyBlob(self.platform, file, exportInfo.destination, options);
|
||||
}
|
||||
else {
|
||||
sounds = await self.exporter.copySound(self.platform, file, exportInfo.destination, options);
|
||||
}
|
||||
if (sounds.files.length === 0) {
|
||||
throw 'Audio file ' + file + ' could not be exported, you have to specify a path to ffmpeg.';
|
||||
}
|
||||
if (!options.notinlist) {
|
||||
parsedFiles.push({ name: exportInfo.name, from: file, type: 'sound', files: sounds.files, file_sizes: sounds.sizes, original_width: undefined, original_height: undefined, readable: undefined });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '.ttf': {
|
||||
let exportInfo = AssetConverter.createExportInfo(fileinfo, false, options, self.exporter.options.from);
|
||||
let fonts: { files: string[], sizes: number[] };
|
||||
if (options.noprocessing) {
|
||||
fonts = await self.exporter.copyBlob(self.platform, file, exportInfo.destination, options);
|
||||
}
|
||||
else {
|
||||
fonts = await self.exporter.copyFont(self.platform, file, exportInfo.destination, options);
|
||||
}
|
||||
if (!options.notinlist) {
|
||||
parsedFiles.push({ name: exportInfo.name, from: file, type: 'font', files: fonts.files, file_sizes: fonts.sizes, original_width: undefined, original_height: undefined, readable: undefined });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '.mp4':
|
||||
case '.webm':
|
||||
case '.mov':
|
||||
case '.wmv':
|
||||
case '.avi': {
|
||||
let exportInfo = AssetConverter.createExportInfo(fileinfo, false, options, self.exporter.options.from);
|
||||
let videos: { files: string[], sizes: number[] };
|
||||
if (options.noprocessing) {
|
||||
videos = await self.exporter.copyBlob(self.platform, file, exportInfo.destination, options);
|
||||
}
|
||||
else {
|
||||
videos = await self.exporter.copyVideo(self.platform, file, exportInfo.destination, options);
|
||||
}
|
||||
if (videos.files.length === 0) {
|
||||
log.error('Video file ' + file + ' could not be exported, you have to specify a path to ffmpeg.');
|
||||
}
|
||||
if (!options.notinlist) {
|
||||
parsedFiles.push({ name: exportInfo.name, from: file, type: 'video', files: videos.files, file_sizes: videos.sizes, original_width: undefined, original_height: undefined, readable: undefined });
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
let exportInfo = AssetConverter.createExportInfo(fileinfo, true, options, self.exporter.options.from);
|
||||
let blobs = await self.exporter.copyBlob(self.platform, file, exportInfo.destination, options);
|
||||
if (!options.notinlist) {
|
||||
parsedFiles.push({ name: exportInfo.name, from: file, type: 'blob', files: blobs.files, file_sizes: blobs.sizes, original_width: undefined, original_height: undefined, readable: undefined });
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.parallelAssetConversion !== 0) {
|
||||
let todo = files.map((file, index) => {
|
||||
return async () => {
|
||||
await convertAsset(file, index);
|
||||
};
|
||||
});
|
||||
|
||||
let processes = this.options.parallelAssetConversion === -1
|
||||
? require('os').cpus().length - 1
|
||||
: this.options.parallelAssetConversion;
|
||||
|
||||
await Throttle.all(todo, {
|
||||
maxInProgress: processes,
|
||||
});
|
||||
} else {
|
||||
let index = 0;
|
||||
for (let file of files) {
|
||||
await convertAsset(file, index);
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fs.ensureDirSync(temp);
|
||||
fs.writeFileSync(cachePath, JSON.stringify(cache), { encoding: 'utf8'});
|
||||
resolve(parsedFiles);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async run(watch: boolean, temp: string): Promise<{ name: string, from: string, type: string, files: string[], file_sizes: number[], original_width: number, original_height: number, readable: boolean }[]> {
|
||||
let files: { name: string, from: string, type: string, files: string[], file_sizes: number[], original_width: number, original_height: number, readable: boolean }[] = [];
|
||||
for (let matcher of this.assetMatchers) {
|
||||
files = files.concat(await this.watch(watch, matcher.match, temp, matcher.options));
|
||||
}
|
||||
return files;
|
||||
}
|
||||
}
|
5
Kha/Tools/khamake/src/AudioApi.ts
Normal file
5
Kha/Tools/khamake/src/AudioApi.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export let AudioApi = {
|
||||
Default: 'default',
|
||||
DirectSound: 'directsound',
|
||||
WASAPI: 'wasapi'
|
||||
};
|
5
Kha/Tools/khamake/src/Color.ts
Normal file
5
Kha/Tools/khamake/src/Color.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export class Color {
|
||||
red: number;
|
||||
green: number;
|
||||
blue: number;
|
||||
}
|
45
Kha/Tools/khamake/src/Converter.ts
Normal file
45
Kha/Tools/khamake/src/Converter.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import * as child_process from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as log from './log';
|
||||
|
||||
export function convert(inFilename: string, outFilename: string, encoder: string, args: Array<string> = null): Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (fs.existsSync(outFilename.toString()) && fs.statSync(outFilename.toString()).mtime.getTime() > fs.statSync(inFilename.toString()).mtime.getTime()) {
|
||||
resolve(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!encoder) {
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let dirend = Math.max(encoder.lastIndexOf('/'), encoder.lastIndexOf('\\'));
|
||||
let firstspace = encoder.indexOf(' ', dirend);
|
||||
let exe = encoder.substr(0, firstspace);
|
||||
let parts = encoder.substr(firstspace + 1).split(' ');
|
||||
let options: string[] = [];
|
||||
for (let i = 0; i < parts.length; ++i) {
|
||||
let foundarg = false;
|
||||
if (args !== null) {
|
||||
for (let arg in args) {
|
||||
if (parts[i] === '{' + arg + '}') {
|
||||
options.push(args[arg]);
|
||||
foundarg = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (foundarg) continue;
|
||||
|
||||
if (parts[i] === '{in}') options.push(inFilename.toString());
|
||||
else if (parts[i] === '{out}') options.push(outFilename.toString());
|
||||
else options.push(parts[i]);
|
||||
}
|
||||
// About stdio ignore: https://stackoverflow.com/a/20792428
|
||||
let process = child_process.spawn(exe, options, {stdio: 'ignore'});
|
||||
process.on('close', (code: number) => {
|
||||
resolve(code === 0);
|
||||
});
|
||||
});
|
||||
}
|
121
Kha/Tools/khamake/src/Exporters/CSharpExporter.ts
Normal file
121
Kha/Tools/khamake/src/Exporters/CSharpExporter.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import {KhaExporter} from './KhaExporter';
|
||||
import {convert} from '../Converter';
|
||||
import {executeHaxe} from '../Haxe';
|
||||
import {Options} from '../Options';
|
||||
import {exportImage} from '../ImageTool';
|
||||
import {Library} from '../Project';
|
||||
const uuid = require('uuid');
|
||||
|
||||
export abstract class CSharpExporter extends KhaExporter {
|
||||
constructor(options: Options) {
|
||||
super(options);
|
||||
fs.removeSync(path.join(this.options.to, this.sysdir() + '-build', 'Sources'));
|
||||
}
|
||||
|
||||
includeFiles(dir: string, baseDir: string) {
|
||||
if (!dir || !fs.existsSync(dir)) return;
|
||||
let files = fs.readdirSync(dir);
|
||||
for (let f in files) {
|
||||
let file = path.join(dir, files[f]);
|
||||
if (fs.existsSync(file) && fs.statSync(file).isDirectory()) this.includeFiles(file, baseDir);
|
||||
else if (file.endsWith('.cs')) {
|
||||
this.p('<Compile Include="' + path.relative(baseDir, file).replace(/\//g, '\\') + '" />', 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
haxeOptions(name: string, targetOptions: any, defines: Array<string>) {
|
||||
defines.push('no-root');
|
||||
defines.push('no-compilation');
|
||||
|
||||
defines.push('sys_' + this.options.target);
|
||||
defines.push('sys_g1');
|
||||
defines.push('sys_g2');
|
||||
defines.push('sys_a1');
|
||||
|
||||
defines.push('kha_cs');
|
||||
defines.push('kha_' + this.options.target);
|
||||
defines.push('kha_' + this.options.target + '_cs');
|
||||
defines.push('kha_g1');
|
||||
defines.push('kha_g2');
|
||||
defines.push('kha_a1');
|
||||
|
||||
return {
|
||||
from: this.options.from,
|
||||
to: path.join(this.sysdir() + '-build', 'Sources'),
|
||||
sources: this.sources,
|
||||
libraries: this.libraries,
|
||||
defines: defines,
|
||||
parameters: this.parameters,
|
||||
haxeDirectory: this.options.haxe,
|
||||
system: this.sysdir(),
|
||||
language: 'cs',
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
name: name,
|
||||
main: this.options.main,
|
||||
};
|
||||
}
|
||||
|
||||
async export(name: string, targetOptions: any, haxeOptions: any): Promise<void> {
|
||||
if (this.projectFiles) {
|
||||
const projectUuid: string = uuid.v4();
|
||||
this.exportSLN(projectUuid);
|
||||
this.exportCsProj(projectUuid);
|
||||
this.exportResources();
|
||||
}
|
||||
}
|
||||
|
||||
exportSLN(projectUuid: string) {
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir() + '-build'));
|
||||
this.writeFile(path.join(this.options.to, this.sysdir() + '-build', 'Project.sln'));
|
||||
const solutionUuid = uuid.v4();
|
||||
|
||||
this.p('Microsoft Visual Studio Solution File, Format Version 11.00');
|
||||
this.p('# Visual Studio 2010');
|
||||
this.p('Project("{' + solutionUuid.toString().toUpperCase() + '}") = "HaxeProject", "Project.csproj", "{' + projectUuid.toString().toUpperCase() + '}"');
|
||||
this.p('EndProject');
|
||||
this.p('Global');
|
||||
this.p('GlobalSection(SolutionConfigurationPlatforms) = preSolution', 1);
|
||||
this.p('Debug|x86 = Debug|x86', 2);
|
||||
this.p('Release|x86 = Release|x86', 2);
|
||||
this.p('EndGlobalSection', 1);
|
||||
this.p('GlobalSection(ProjectConfigurationPlatforms) = postSolution', 1);
|
||||
this.p('{' + projectUuid.toString().toUpperCase() + '}.Debug|x86.ActiveCfg = Debug|x86', 2);
|
||||
this.p('{' + projectUuid.toString().toUpperCase() + '}.Debug|x86.Build.0 = Debug|x86', 2);
|
||||
this.p('{' + projectUuid.toString().toUpperCase() + '}.Release|x86.ActiveCfg = Release|x86', 2);
|
||||
this.p('{' + projectUuid.toString().toUpperCase() + '}.Release|x86.Build.0 = Release|x86', 2);
|
||||
this.p('EndGlobalSection', 1);
|
||||
this.p('GlobalSection(SolutionProperties) = preSolution', 1);
|
||||
this.p('HideSolutionNode = FALSE', 2);
|
||||
this.p('EndGlobalSection', 1);
|
||||
this.p('EndGlobal');
|
||||
this.closeFile();
|
||||
}
|
||||
|
||||
abstract backend(): string;
|
||||
|
||||
abstract exportCsProj(projectUuid: string): void;
|
||||
|
||||
abstract exportResources(): void;
|
||||
|
||||
async copySound(platform: string, from: string, to: string) {
|
||||
return { files: [to], sizes: [1] };
|
||||
}
|
||||
|
||||
async copyImage(platform: string, from: string, to: string, asset: any, cache: any) {
|
||||
let format = await exportImage(this.options.kha, this.options.kraffiti, from, path.join(this.options.to, this.sysdir(), to), asset, undefined, false, false, cache);
|
||||
return { files: [to + '.' + format], sizes: [1] };
|
||||
}
|
||||
|
||||
async copyBlob(platform: string, from: string, to: string) {
|
||||
fs.copySync(from, path.join(this.options.to, this.sysdir(), to), { overwrite: true });
|
||||
return { files: [to], sizes: [1] };
|
||||
}
|
||||
|
||||
async copyVideo(platform: string, from: string, to: string) {
|
||||
return { files: [to], sizes: [1] };
|
||||
}
|
||||
}
|
9
Kha/Tools/khamake/src/Exporters/DebugHtml5Exporter.ts
Normal file
9
Kha/Tools/khamake/src/Exporters/DebugHtml5Exporter.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import {Html5Exporter} from './Html5Exporter';
|
||||
import {Options} from '../Options';
|
||||
|
||||
export class DebugHtml5Exporter extends Html5Exporter {
|
||||
constructor(options: Options) {
|
||||
super(options);
|
||||
this.isDebug = true;
|
||||
}
|
||||
}
|
88
Kha/Tools/khamake/src/Exporters/EmptyExporter.ts
Normal file
88
Kha/Tools/khamake/src/Exporters/EmptyExporter.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import * as child_process from 'child_process';
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import {KhaExporter} from './KhaExporter';
|
||||
import {convert} from '../Converter';
|
||||
import {executeHaxe} from '../Haxe';
|
||||
import {Options} from '../Options';
|
||||
import {exportImage} from '../ImageTool';
|
||||
import * as log from '../log';
|
||||
import {Library} from '../Project';
|
||||
|
||||
export class EmptyExporter extends KhaExporter {
|
||||
constructor(options: Options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
backend(): string {
|
||||
return 'Empty';
|
||||
}
|
||||
|
||||
haxeOptions(name: string, targetOptions: any, defines: Array<string>) {
|
||||
defines.push('sys_g1');
|
||||
defines.push('sys_g2');
|
||||
defines.push('sys_g3');
|
||||
defines.push('sys_g4');
|
||||
defines.push('sys_a1');
|
||||
defines.push('sys_a2');
|
||||
|
||||
defines.push('kha_g1');
|
||||
defines.push('kha_g2');
|
||||
defines.push('kha_g3');
|
||||
defines.push('kha_g4');
|
||||
defines.push('kha_a1');
|
||||
defines.push('kha_a2');
|
||||
|
||||
return {
|
||||
from: this.options.from,
|
||||
to: path.join(this.sysdir(), 'docs.xml'),
|
||||
sources: this.sources,
|
||||
libraries: this.libraries,
|
||||
defines: defines,
|
||||
parameters: this.parameters,
|
||||
haxeDirectory: this.options.haxe,
|
||||
system: this.sysdir(),
|
||||
language: 'xml',
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
name: name,
|
||||
main: this.options.main,
|
||||
};
|
||||
}
|
||||
|
||||
async export(name: string, _targetOptions: any, haxeOptions: any): Promise<void> {
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir()));
|
||||
|
||||
try {
|
||||
// Remove any @:export first
|
||||
await executeHaxe(this.options.to, this.options.haxe, ['project-' + this.sysdir() + '.hxml']);
|
||||
let doxresult = child_process.spawnSync('haxelib', ['run', 'dox', '-in', 'kha.*', '-i', path.join(this.sysdir(), 'docs.xml')], { env: process.env, cwd: path.normalize(this.options.to) });
|
||||
if (doxresult.stdout.toString() !== '') {
|
||||
log.info(doxresult.stdout.toString());
|
||||
}
|
||||
|
||||
if (doxresult.stderr.toString() !== '') {
|
||||
log.error(doxresult.stderr.toString());
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
async copySound(platform: string, from: string, to: string) {
|
||||
return { files: [''], sizes: [0] };
|
||||
}
|
||||
|
||||
async copyImage(platform: string, from: string, to: string, asset: any) {
|
||||
return { files: [''], sizes: [0] };
|
||||
}
|
||||
|
||||
async copyBlob(platform: string, from: string, to: string) {
|
||||
return { files: [''], sizes: [0] };
|
||||
}
|
||||
|
||||
async copyVideo(platform: string, from: string, to: string) {
|
||||
return { files: [''], sizes: [0] };
|
||||
}
|
||||
}
|
36
Kha/Tools/khamake/src/Exporters/Exporter.ts
Normal file
36
Kha/Tools/khamake/src/Exporters/Exporter.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import * as fs from 'fs-extra';
|
||||
|
||||
export class Exporter {
|
||||
out: number;
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
writeFile(file: string) {
|
||||
this.out = fs.openSync(file, 'w');
|
||||
}
|
||||
|
||||
closeFile() {
|
||||
fs.closeSync(this.out);
|
||||
}
|
||||
|
||||
p(line: string = '', indent: number = 0) {
|
||||
let tabs = '';
|
||||
for (let i = 0; i < indent; ++i) tabs += '\t';
|
||||
let data = Buffer.from(tabs + line + '\n');
|
||||
fs.writeSync(this.out, data, 0, data.length, null);
|
||||
}
|
||||
|
||||
copyFile(from: string, to: string) {
|
||||
fs.copySync(from, to, { overwrite: true });
|
||||
}
|
||||
|
||||
copyDirectory(from: string, to: string) {
|
||||
fs.copySync(from, to, { overwrite: true });
|
||||
}
|
||||
|
||||
createDirectory(dir: string) {
|
||||
fs.ensureDirSync(dir);
|
||||
}
|
||||
}
|
148
Kha/Tools/khamake/src/Exporters/FlashExporter.ts
Normal file
148
Kha/Tools/khamake/src/Exporters/FlashExporter.ts
Normal file
@ -0,0 +1,148 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import {KhaExporter} from './KhaExporter';
|
||||
import {convert} from '../Converter';
|
||||
import {executeHaxe} from '../Haxe';
|
||||
import {Options} from '../Options';
|
||||
import {exportImage} from '../ImageTool';
|
||||
import {Library} from '../Project';
|
||||
|
||||
function adjustFilename(filename: string): string {
|
||||
filename = filename.replace(/\./g, '_');
|
||||
filename = filename.replace(/-/g, '_');
|
||||
filename = filename.replace(/\//g, '_');
|
||||
return filename;
|
||||
}
|
||||
|
||||
export class FlashExporter extends KhaExporter {
|
||||
images: Array<string>;
|
||||
sounds: Array<string>;
|
||||
blobs: Array<string>;
|
||||
|
||||
constructor(options: Options) {
|
||||
super(options);
|
||||
this.images = [];
|
||||
this.sounds = [];
|
||||
this.blobs = [];
|
||||
}
|
||||
|
||||
backend(): string {
|
||||
return 'Flash';
|
||||
}
|
||||
|
||||
haxeOptions(name: string, targetOptions: any, defines: Array<string>) {
|
||||
defines.push('swf-script-timeout=60');
|
||||
|
||||
defines.push('sys_' + this.options.target);
|
||||
defines.push('sys_g1');
|
||||
defines.push('sys_g2');
|
||||
defines.push('sys_g3');
|
||||
defines.push('sys_g4');
|
||||
defines.push('sys_a1');
|
||||
defines.push('sys_a2');
|
||||
|
||||
defines.push('kha_' + this.options.target);
|
||||
defines.push('kha_stage3d');
|
||||
defines.push('kha_g1');
|
||||
defines.push('kha_g2');
|
||||
defines.push('kha_g3');
|
||||
defines.push('kha_g4');
|
||||
defines.push('kha_a1');
|
||||
defines.push('kha_a2');
|
||||
|
||||
if (this.options.embedflashassets) defines.push('KHA_EMBEDDED_ASSETS');
|
||||
|
||||
let defaultFlashOptions = {
|
||||
framerate : 60,
|
||||
stageBackground : 'ffffff',
|
||||
swfVersion : '16.0'
|
||||
};
|
||||
|
||||
let flashOptions = targetOptions ? (targetOptions.flash ? targetOptions.flash : defaultFlashOptions) : defaultFlashOptions;
|
||||
|
||||
return {
|
||||
from: this.options.from,
|
||||
to: path.join(this.sysdir(), 'kha.swf'),
|
||||
sources: this.sources,
|
||||
libraries: this.libraries,
|
||||
defines: defines,
|
||||
parameters: this.parameters,
|
||||
haxeDirectory: this.options.haxe,
|
||||
system: this.sysdir(),
|
||||
language: 'as',
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
name: name,
|
||||
main: this.options.main,
|
||||
framerate: 'framerate' in flashOptions ? flashOptions.framerate : defaultFlashOptions.framerate,
|
||||
stageBackground: 'stageBackground' in flashOptions ? flashOptions.stageBackground : defaultFlashOptions.stageBackground,
|
||||
swfVersion : 'swfVersion' in flashOptions ? flashOptions.swfVersion : defaultFlashOptions.swfVersion,
|
||||
};
|
||||
}
|
||||
|
||||
async export(name: string, targetOptions: any, haxeOptions: any): Promise<void> {
|
||||
if (this.options.embedflashassets) {
|
||||
this.writeFile(path.join(this.options.to, '..', 'Sources', 'Assets.hx'));
|
||||
|
||||
this.p('package;');
|
||||
this.p();
|
||||
this.p('import flash.display.BitmapData;');
|
||||
this.p('import flash.media.Sound;');
|
||||
this.p('import flash.utils.ByteArray;');
|
||||
this.p();
|
||||
|
||||
for (let image of this.images) {
|
||||
this.p('@:bitmap("flash/' + image + '") class Assets_' + adjustFilename(image) + ' extends BitmapData { }');
|
||||
}
|
||||
|
||||
this.p();
|
||||
|
||||
for (let sound of this.sounds) {
|
||||
this.p('@:file("flash/' + sound + '") class Assets_' + adjustFilename(sound) + ' extends ByteArray { }');
|
||||
}
|
||||
|
||||
this.p();
|
||||
|
||||
for (let blob of this.blobs) {
|
||||
this.p('@:file("flash/' + blob + '") class Assets_' + adjustFilename(blob) + ' extends ByteArray { }');
|
||||
}
|
||||
|
||||
this.p();
|
||||
this.p('class Assets {');
|
||||
this.p('public static function visit(): Void {', 1);
|
||||
this.p('', 2);
|
||||
this.p('}', 1);
|
||||
this.p('}');
|
||||
|
||||
this.closeFile();
|
||||
}
|
||||
}
|
||||
|
||||
async copySound(platform: string, from: string, to: string) {
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir(), path.dirname(to)));
|
||||
await convert(from, path.join(this.options.to, this.sysdir(), to + '.mp3'), this.options.mp3);
|
||||
return { files: [to + '.mp3'], sizes: [1] };
|
||||
}
|
||||
|
||||
async copyImage(platform: string, from: string, to: string, asset: any, cache: any) {
|
||||
let format = await exportImage(this.options.kha, this.options.kraffiti, from, path.join(this.options.to, this.sysdir(), to), asset, undefined, false, false, cache);
|
||||
if (this.options.embedflashassets) this.images.push(to + '.' + format);
|
||||
return { files: [to + '.' + format], sizes: [1] };
|
||||
}
|
||||
|
||||
async copyBlob(platform: string, from: string, to: string) {
|
||||
fs.copySync(from.toString(), path.join(this.options.to, this.sysdir(), to), { overwrite: true });
|
||||
if (this.options.embedflashassets) this.blobs.push(to);
|
||||
return { files: [to], sizes: [1] };
|
||||
}
|
||||
|
||||
async copyVideo(platform: string, from: string, to: string) {
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir(), path.dirname(to)));
|
||||
await convert(from, path.join(this.options.to, this.sysdir(), to + '.mp4'), this.options.h264);
|
||||
return { files: [to + '.mp4'], sizes: [1] };
|
||||
}
|
||||
|
||||
addShader(shader: string) {
|
||||
if (this.options.embedflashassets) this.blobs.push(shader);
|
||||
}
|
||||
}
|
274
Kha/Tools/khamake/src/Exporters/Html5Exporter.ts
Normal file
274
Kha/Tools/khamake/src/Exporters/Html5Exporter.ts
Normal file
@ -0,0 +1,274 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import {KhaExporter} from './KhaExporter';
|
||||
import {convert} from '../Converter';
|
||||
import {executeHaxe} from '../Haxe';
|
||||
import {Options} from '../Options';
|
||||
import {exportImage} from '../ImageTool';
|
||||
import {Library} from '../Project';
|
||||
import {VrApi} from '../VrApi';
|
||||
|
||||
export class Html5Exporter extends KhaExporter {
|
||||
width: number;
|
||||
height: number;
|
||||
isDebug: boolean;
|
||||
|
||||
constructor(options: Options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
backend(): string {
|
||||
return 'HTML5';
|
||||
}
|
||||
|
||||
isADebugTarget() {
|
||||
return this.isDebug;
|
||||
}
|
||||
|
||||
isDebugHtml5() {
|
||||
return this.sysdir() === 'debug-html5';
|
||||
}
|
||||
|
||||
isNode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
isHtml5Worker() {
|
||||
return this.sysdir() === 'html5worker';
|
||||
}
|
||||
|
||||
haxeOptions(name: string, targetOptions: any, defines: Array<string>) {
|
||||
defines.push('sys_g1');
|
||||
defines.push('sys_g2');
|
||||
defines.push('sys_g3');
|
||||
defines.push('sys_a1');
|
||||
defines.push('sys_a2');
|
||||
|
||||
defines.push('kha_js');
|
||||
defines.push('kha_g1');
|
||||
defines.push('kha_g2');
|
||||
defines.push('kha_g3');
|
||||
defines.push('kha_a1');
|
||||
defines.push('kha_a2');
|
||||
|
||||
if (targetOptions.html5.noKeyboard) {
|
||||
defines.push('kha_no_keyboard');
|
||||
}
|
||||
|
||||
if (targetOptions.html5.disableContextMenu) {
|
||||
defines.push('kha_disable_context_menu');
|
||||
}
|
||||
|
||||
if (this.options.vr === VrApi.WebVR) {
|
||||
defines.push('kha_webvr');
|
||||
}
|
||||
|
||||
let canvasId = targetOptions.html5.canvasId == null ? 'khanvas' : targetOptions.html5.canvasId;
|
||||
|
||||
defines.push('canvas_id=' + canvasId);
|
||||
|
||||
let scriptName = this.isHtml5Worker() ? 'khaworker' : 'kha';
|
||||
if (targetOptions.html5.scriptName != null && !(this.isNode() || this.isDebugHtml5())) {
|
||||
scriptName = targetOptions.html5.scriptName;
|
||||
}
|
||||
|
||||
defines.push('script_name=' + scriptName);
|
||||
|
||||
let webgl = targetOptions.html5.webgl == null ? true : targetOptions.html5.webgl;
|
||||
|
||||
if (webgl) {
|
||||
defines.push('sys_g4');
|
||||
defines.push('kha_g4');
|
||||
defines.push('kha_webgl');
|
||||
} else {
|
||||
defines.push('kha_html5_canvas');
|
||||
}
|
||||
|
||||
if (this.isNode()) {
|
||||
defines.push('nodejs');
|
||||
|
||||
defines.push('sys_node');
|
||||
defines.push('sys_server');
|
||||
|
||||
defines.push('kha_node');
|
||||
defines.push('kha_server');
|
||||
}
|
||||
else {
|
||||
defines.push('sys_' + this.options.target);
|
||||
|
||||
defines.push('kha_' + this.options.target);
|
||||
defines.push('kha_' + this.options.target + '_js');
|
||||
|
||||
defines.push('sys_html5');
|
||||
defines.push('kha_html5');
|
||||
defines.push('kha_html5_js');
|
||||
}
|
||||
|
||||
if (this.isADebugTarget()) {
|
||||
this.parameters.push('-debug');
|
||||
|
||||
defines.push('sys_debug_html5');
|
||||
|
||||
defines.push('kha_debug_html5');
|
||||
defines.push('kha_html5');
|
||||
}
|
||||
|
||||
if (this.isHtml5Worker()) {
|
||||
defines.push('js-classic');
|
||||
}
|
||||
|
||||
return {
|
||||
from: this.options.from.toString(),
|
||||
to: path.join(this.sysdir(), scriptName + '.js'),
|
||||
sources: this.sources,
|
||||
libraries: this.libraries,
|
||||
defines: defines,
|
||||
parameters: this.parameters,
|
||||
haxeDirectory: this.options.haxe,
|
||||
system: this.sysdir(),
|
||||
language: 'js',
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
name: name,
|
||||
main: this.options.main,
|
||||
};
|
||||
}
|
||||
|
||||
async export(name: string, _targetOptions: any, haxeOptions: any): Promise<void> {
|
||||
let targetOptions = {
|
||||
canvasId: 'khanvas',
|
||||
scriptName: this.isHtml5Worker() ? 'khaworker' : 'kha',
|
||||
unsafeEval: false,
|
||||
expose: ''
|
||||
};
|
||||
|
||||
if (_targetOptions != null && _targetOptions.html5 != null) {
|
||||
let userOptions = _targetOptions.html5;
|
||||
if (userOptions.canvasId != null) targetOptions.canvasId = userOptions.canvasId;
|
||||
if (userOptions.scriptName != null) targetOptions.scriptName = userOptions.scriptName;
|
||||
if (userOptions.unsafeEval != null) targetOptions.unsafeEval = userOptions.unsafeEval;
|
||||
if (userOptions.expose != null) targetOptions.expose = userOptions.expose;
|
||||
}
|
||||
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir()));
|
||||
|
||||
if (this.isADebugTarget()) { // support custom debug-html5 based targets
|
||||
let electron = path.join(this.options.to, this.sysdir(), 'electron.js');
|
||||
let protoelectron = fs.readFileSync(path.join(__dirname, '..', '..', 'Data', 'debug-html5', 'electron.js'), {encoding: 'utf8'});
|
||||
protoelectron = protoelectron.replace(/{Width}/g, '' + this.width);
|
||||
protoelectron = protoelectron.replace(/{Height}/g, '' + this.height);
|
||||
protoelectron = protoelectron.replace(/{ext}/g, process.platform === 'win32' ? '\'.ico\'' : '\'.png\'');
|
||||
fs.writeFileSync(electron.toString(), protoelectron);
|
||||
|
||||
let pack = path.join(this.options.to, this.sysdir(), 'package.json');
|
||||
let protopackage = fs.readFileSync(path.join(__dirname, '..', '..', 'Data', 'debug-html5', 'package.json'), {encoding: 'utf8'});
|
||||
protopackage = protopackage.replace(/{Name}/g, name);
|
||||
fs.writeFileSync(pack.toString(), protopackage);
|
||||
|
||||
let index = path.join(this.options.to, this.sysdir(), 'index.html');
|
||||
let protoindex = fs.readFileSync(path.join(__dirname, '..', '..', 'Data', 'debug-html5', 'index.html'), {encoding: 'utf8'});
|
||||
protoindex = protoindex.replace(/{Name}/g, name);
|
||||
protoindex = protoindex.replace(/{Width}/g, '' + this.width);
|
||||
protoindex = protoindex.replace(/{Height}/g, '' + this.height);
|
||||
protoindex = protoindex.replace(/{CanvasId}/g, '' + targetOptions.canvasId);
|
||||
protoindex = protoindex.replace(/{ScriptName}/g, '' + targetOptions.scriptName);
|
||||
protoindex = protoindex.replace(/{UnsafeEval}/g, targetOptions.unsafeEval ? '\'unsafe-eval\'' : '');
|
||||
fs.writeFileSync(index.toString(), protoindex);
|
||||
|
||||
let preload = path.join(this.options.to, this.sysdir(), 'preload.js');
|
||||
let protopreload = fs.readFileSync(path.join(__dirname, '..', '..', 'Data', 'debug-html5', 'preload.js'), {encoding: 'utf8'});
|
||||
protopreload = protopreload.replace(/{Expose}/g, targetOptions.expose);
|
||||
fs.writeFileSync(preload.toString(), protopreload);
|
||||
}
|
||||
else if (this.isNode()) {
|
||||
let pack = path.join(this.options.to, this.sysdir(), 'package.json');
|
||||
let protopackage = fs.readFileSync(path.join(__dirname, '..', '..', 'Data', 'node', 'package.json'), 'utf8');
|
||||
protopackage = protopackage.replace(/{Name}/g, name);
|
||||
fs.writeFileSync(pack, protopackage);
|
||||
|
||||
let protoserver = fs.readFileSync(path.join(__dirname, '..', '..', 'Data', 'node', 'server.js'), 'utf8');
|
||||
fs.writeFileSync(path.join(this.options.to, this.sysdir(), 'server.js'), protoserver);
|
||||
}
|
||||
else if (!this.isHtml5Worker()) {
|
||||
let index = path.join(this.options.to, this.sysdir(), 'index.html');
|
||||
if (!fs.existsSync(index)) {
|
||||
let protoindex = fs.readFileSync(path.join(__dirname, '..', '..', 'Data', 'html5', 'index.html'), {encoding: 'utf8'});
|
||||
protoindex = protoindex.replace(/{Name}/g, name);
|
||||
protoindex = protoindex.replace(/{Width}/g, '' + this.width);
|
||||
protoindex = protoindex.replace(/{Height}/g, '' + this.height);
|
||||
protoindex = protoindex.replace(/{CanvasId}/g, '' + targetOptions.canvasId);
|
||||
protoindex = protoindex.replace(/{ScriptName}/g, '' + targetOptions.scriptName);
|
||||
fs.writeFileSync(index.toString(), protoindex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*copyMusic(platform, from, to, encoders, callback) {
|
||||
Files.createDirectories(this.directory.resolve(this.sysdir()).resolve(to).parent());
|
||||
Converter.convert(from, this.directory.resolve(this.sysdir()).resolve(to + '.ogg'), encoders.oggEncoder, (ogg) => {
|
||||
Converter.convert(from, this.directory.resolve(this.sysdir()).resolve(to + '.mp4'), encoders.aacEncoder, (mp4) => {
|
||||
var files = [];
|
||||
if (ogg) files.push(to + '.ogg');
|
||||
if (mp4) files.push(to + '.mp4');
|
||||
callback(files);
|
||||
});
|
||||
});
|
||||
}*/
|
||||
|
||||
async copySound(platform: string, from: string, to: string, options: any) {
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir(), path.dirname(to)));
|
||||
let ogg = await convert(from, path.join(this.options.to, this.sysdir(), to + '.ogg'), this.options.ogg);
|
||||
let ogg_size = (await fs.stat(path.join(this.options.to, this.sysdir(), to + '.ogg'))).size;
|
||||
let mp4 = false;
|
||||
let mp4_size = 0;
|
||||
let mp3 = false;
|
||||
let mp3_size = 0;
|
||||
if (!this.isDebugHtml5()) {
|
||||
mp4 = await convert(from, path.join(this.options.to, this.sysdir(), to + '.mp4'), this.options.aac);
|
||||
if (mp4) {
|
||||
mp4_size = (await fs.stat(path.join(this.options.to, this.sysdir(), to + '.mp4'))).size;
|
||||
}
|
||||
if (!mp4) {
|
||||
mp3 = await convert(from, path.join(this.options.to, this.sysdir(), to + '.mp3'), this.options.mp3);
|
||||
mp3_size = (await fs.stat(path.join(this.options.to, this.sysdir(), to + '.mp3'))).size;
|
||||
}
|
||||
}
|
||||
let files: string[] = [];
|
||||
let sizes: number[] = [];
|
||||
if (ogg) { files.push(to + '.ogg'); sizes.push(ogg_size); }
|
||||
if (mp4) { files.push(to + '.mp4'); sizes.push(mp4_size); }
|
||||
if (mp3) { files.push(to + '.mp3'); sizes.push(mp3_size); }
|
||||
return { files: files, sizes: sizes };
|
||||
}
|
||||
|
||||
async copyImage(platform: string, from: string, to: string, options: any, cache: any) {
|
||||
let format = await exportImage(this.options.kha, this.options.kraffiti, from, path.join(this.options.to, this.sysdir(), to), options, undefined, false, false, cache);
|
||||
let stat = await fs.stat(path.join(this.options.to, this.sysdir(), to + '.' + format));
|
||||
let size = stat.size;
|
||||
return { files: [to + '.' + format], sizes: [size]};
|
||||
}
|
||||
|
||||
async copyBlob(platform: string, from: string, to: string, options: any) {
|
||||
fs.copySync(from.toString(), path.join(this.options.to, this.sysdir(), to), { overwrite: true, dereference: true });
|
||||
let stat = await fs.stat(path.join(this.options.to, this.sysdir(), to));
|
||||
let size = stat.size;
|
||||
return { files: [to], sizes: [size]};
|
||||
}
|
||||
|
||||
async copyVideo(platform: string, from: string, to: string, options: any) {
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir(), path.dirname(to)));
|
||||
let mp4 = false;
|
||||
let mp4_size = 0;
|
||||
if (!this.isDebugHtml5()) {
|
||||
mp4 = await convert(from, path.join(this.options.to, this.sysdir(), to + '.mp4'), this.options.h264);
|
||||
mp4_size = (await fs.stat(path.join(this.options.to, this.sysdir(), to + '.mp4'))).size;
|
||||
}
|
||||
let webm = await convert(from, path.join(this.options.to, this.sysdir(), to + '.webm'), this.options.webm);
|
||||
let webm_size = (await fs.stat(path.join(this.options.to, this.sysdir(), to + '.webm'))).size;
|
||||
let files: string[] = [];
|
||||
let sizes: number[] = [];
|
||||
if (mp4) { files.push(to + '.mp4'); sizes.push(mp4_size); }
|
||||
if (webm) { files.push(to + '.webm'); sizes.push(webm_size); }
|
||||
return { files: files, sizes: sizes };
|
||||
}
|
||||
}
|
13
Kha/Tools/khamake/src/Exporters/Html5WorkerExporter.ts
Normal file
13
Kha/Tools/khamake/src/Exporters/Html5WorkerExporter.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import * as path from 'path';
|
||||
import {Html5Exporter} from './Html5Exporter';
|
||||
import {Options} from '../Options';
|
||||
|
||||
export class Html5WorkerExporter extends Html5Exporter {
|
||||
constructor(options: Options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
backend(): string {
|
||||
return 'HTML5-Worker';
|
||||
}
|
||||
}
|
117
Kha/Tools/khamake/src/Exporters/JavaExporter.ts
Normal file
117
Kha/Tools/khamake/src/Exporters/JavaExporter.ts
Normal file
@ -0,0 +1,117 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import {KhaExporter} from './KhaExporter';
|
||||
import {convert} from '../Converter';
|
||||
import {executeHaxe} from '../Haxe';
|
||||
import {Options} from '../Options';
|
||||
import {exportImage} from '../ImageTool';
|
||||
import {Library} from '../Project';
|
||||
|
||||
export class JavaExporter extends KhaExporter {
|
||||
constructor(options: Options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
haxeOptions(name: string, targetOptions: any, defines: Array<string>) {
|
||||
const sources = path.join(this.options.to, this.sysdir(), 'Sources');
|
||||
if (fs.existsSync(sources)) {
|
||||
fs.removeSync(sources);
|
||||
}
|
||||
|
||||
defines.push('no-compilation');
|
||||
|
||||
defines.push('sys_' + this.options.target);
|
||||
defines.push('sys_g1');
|
||||
defines.push('sys_g2');
|
||||
defines.push('sys_a1');
|
||||
|
||||
defines.push('kha_' + this.options.target);
|
||||
if (this.options.target !== 'java') {
|
||||
defines.push('kha_java');
|
||||
defines.push('kha_' + this.options.target + '_java');
|
||||
}
|
||||
defines.push('kha_g1');
|
||||
defines.push('kha_g2');
|
||||
defines.push('kha_a1');
|
||||
|
||||
return {
|
||||
from: this.options.from,
|
||||
to: path.join(this.sysdir(), 'Sources'),
|
||||
sources: this.sources,
|
||||
libraries: this.libraries,
|
||||
defines: defines,
|
||||
parameters: this.parameters,
|
||||
haxeDirectory: this.options.haxe,
|
||||
system: this.sysdir(),
|
||||
language: 'java',
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
name: name,
|
||||
main: this.options.main,
|
||||
};
|
||||
}
|
||||
|
||||
async export(name: string, targetOptions: any, haxeOptions: any): Promise<void> {
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir()));
|
||||
this.exportEclipseProject();
|
||||
}
|
||||
|
||||
backend() {
|
||||
return 'Java';
|
||||
}
|
||||
|
||||
exportEclipseProject() {
|
||||
this.writeFile(path.join(this.options.to, this.sysdir(), '.classpath'));
|
||||
this.p('<?xml version="1.0" encoding="UTF-8"?>');
|
||||
this.p('<classpath>');
|
||||
this.p('\t<classpathentry kind="src" path="Sources/src"/>');
|
||||
this.p('\t<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>');
|
||||
this.p('\t<classpathentry kind="output" path="bin"/>');
|
||||
this.p('</classpath>');
|
||||
this.closeFile();
|
||||
|
||||
this.writeFile(path.join(this.options.to, this.sysdir(), '.project'));
|
||||
this.p('<?xml version="1.0" encoding="UTF-8"?>');
|
||||
this.p('<projectDescription>');
|
||||
this.p('\t<name>' + path.parse(this.options.to).name + '</name>');
|
||||
this.p('\t<comment></comment>');
|
||||
this.p('\t<projects>');
|
||||
this.p('\t</projects>');
|
||||
this.p('\t<buildSpec>');
|
||||
this.p('\t\t<buildCommand>');
|
||||
this.p('\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>');
|
||||
this.p('\t\t\t<arguments>');
|
||||
this.p('\t\t\t</arguments>');
|
||||
this.p('\t\t</buildCommand>');
|
||||
this.p('\t</buildSpec>');
|
||||
this.p('\t<natures>');
|
||||
this.p('\t\t<nature>org.eclipse.jdt.core.javanature</nature>');
|
||||
this.p('\t</natures>');
|
||||
this.p('</projectDescription>');
|
||||
this.closeFile();
|
||||
}
|
||||
|
||||
/*copyMusic(platform, from, to, encoders) {
|
||||
this.copyFile(from, this.directory.resolve(this.sysdir()).resolve(to + '.wav'));
|
||||
callback([to + '.wav']);
|
||||
}*/
|
||||
|
||||
async copySound(platform: string, from: string, to: string) {
|
||||
fs.copySync(from.toString(), path.join(this.options.to, this.sysdir(), to + '.wav'), { overwrite: true });
|
||||
return { files: [to + '.wav'], sizes: [1] };
|
||||
}
|
||||
|
||||
async copyImage(platform: string, from: string, to: string, asset: any, cache: any) {
|
||||
let format = await exportImage(this.options.kha, this.options.kraffiti, from, path.join(this.options.to, this.sysdir(), to), asset, undefined, false, false, cache);
|
||||
return { files: [to + '.' + format], sizes: [1] };
|
||||
}
|
||||
|
||||
async copyBlob(platform: string, from: string, to: string) {
|
||||
fs.copySync(from.toString(), path.join(this.options.to, this.sysdir(), to), { overwrite: true });
|
||||
return { files: [to], sizes: [1] };
|
||||
}
|
||||
|
||||
async copyVideo(platform: string, from: string, to: string) {
|
||||
return { files: [to], sizes: [1] };
|
||||
}
|
||||
}
|
101
Kha/Tools/khamake/src/Exporters/KhaExporter.ts
Normal file
101
Kha/Tools/khamake/src/Exporters/KhaExporter.ts
Normal file
@ -0,0 +1,101 @@
|
||||
import * as path from 'path';
|
||||
import {convert} from '../Converter';
|
||||
import {Exporter} from './Exporter';
|
||||
import {Options} from '../Options';
|
||||
import {Library} from '../Project';
|
||||
|
||||
export abstract class KhaExporter extends Exporter {
|
||||
width: number;
|
||||
height: number;
|
||||
sources: string[];
|
||||
libraries: Library[];
|
||||
name: string;
|
||||
safename: string;
|
||||
options: Options;
|
||||
projectFiles: boolean;
|
||||
parameters: string[];
|
||||
systemDirectory: string;
|
||||
|
||||
constructor(options: Options) {
|
||||
super();
|
||||
this.options = options;
|
||||
this.width = 640;
|
||||
this.height = 480;
|
||||
this.sources = [];
|
||||
this.libraries = [];
|
||||
this.addSourceDirectory(path.join(options.kha, 'Sources'));
|
||||
this.projectFiles = !options.noproject;
|
||||
this.parameters = [];
|
||||
// this.parameters = ['--macro kha.internal.GraphicsBuilder.build("' + this.backend().toLowerCase() + '")'];
|
||||
this.addSourceDirectory(path.join(options.kha, 'Backends', this.backend()));
|
||||
}
|
||||
|
||||
sysdir(): string {
|
||||
return this.systemDirectory;
|
||||
}
|
||||
|
||||
abstract backend(): string;
|
||||
|
||||
abstract haxeOptions(name: string, targetOptions: any, defines: Array<string>): any;
|
||||
|
||||
async export(name: string, targetOptions: any, haxeOptions: any): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
reject('Called an abstract function');
|
||||
});
|
||||
}
|
||||
|
||||
setWidthAndHeight(width: number, height: number): void {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
setName(name: string): void {
|
||||
this.name = name;
|
||||
this.safename = name.replace(/ /g, '-');
|
||||
}
|
||||
|
||||
setSystemDirectory(systemDirectory: string): void {
|
||||
this.systemDirectory = systemDirectory;
|
||||
}
|
||||
|
||||
addShader(shader: string): void {
|
||||
|
||||
}
|
||||
|
||||
addSourceDirectory(path: string): void {
|
||||
this.sources.push(path);
|
||||
}
|
||||
|
||||
addLibrary(library: Library): void {
|
||||
this.libraries.push(library);
|
||||
}
|
||||
|
||||
removeSourceDirectory(path: string): void {
|
||||
for (let i = 0; i < this.sources.length; ++i) {
|
||||
if (this.sources[i] === path) {
|
||||
this.sources.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async copyImage(platform: string, from: string, to: string, options: any, cache: any): Promise<{ files: Array<string>, sizes: Array<number>}> {
|
||||
return {files: [], sizes: []};
|
||||
}
|
||||
|
||||
async copySound(platform: string, from: string, to: string, options: any): Promise<{ files: Array<string>, sizes: Array<number>}> {
|
||||
return {files: [], sizes: []};
|
||||
}
|
||||
|
||||
async copyVideo(platform: string, from: string, to: string, options: any): Promise<{ files: Array<string>, sizes: Array<number>}> {
|
||||
return {files: [], sizes: []};
|
||||
}
|
||||
|
||||
async copyBlob(platform: string, from: string, to: string, options: any): Promise<{ files: Array<string>, sizes: Array<number>}> {
|
||||
return {files: [], sizes: []};
|
||||
}
|
||||
|
||||
async copyFont(platform: string, from: string, to: string, options: any): Promise<{ files: Array<string>, sizes: Array<number>}> {
|
||||
return await this.copyBlob(platform, from, to + '.ttf', options);
|
||||
}
|
||||
}
|
152
Kha/Tools/khamake/src/Exporters/KincExporter.ts
Normal file
152
Kha/Tools/khamake/src/Exporters/KincExporter.ts
Normal file
@ -0,0 +1,152 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import * as defaults from '../defaults';
|
||||
import {KhaExporter} from './KhaExporter';
|
||||
import {convert} from '../Converter';
|
||||
import {executeHaxe} from '../Haxe';
|
||||
import {GraphicsApi} from '../GraphicsApi';
|
||||
import {Platform} from '../Platform';
|
||||
import {exportImage} from '../ImageTool';
|
||||
import {Options} from '../Options';
|
||||
import {Library} from '../Project';
|
||||
|
||||
export class KincExporter extends KhaExporter {
|
||||
slowgc: boolean;
|
||||
|
||||
constructor(options: Options) {
|
||||
super(options);
|
||||
this.slowgc = options.slowgc;
|
||||
// Files.removeDirectory(this.directory.resolve(Paths.get(this.sysdir() + "-build", "Sources")));
|
||||
}
|
||||
|
||||
backend(): string {
|
||||
return 'Kinc-hxcpp';
|
||||
}
|
||||
|
||||
haxeOptions(name: string, targetOptions: any, defines: Array<string>) {
|
||||
defines.push('no-compilation');
|
||||
defines.push('include-prefix=hxinc');
|
||||
|
||||
if (!this.slowgc) {
|
||||
defines.push('HXCPP_GC_GENERATIONAL');
|
||||
}
|
||||
|
||||
defines.push('sys_' + this.options.target);
|
||||
defines.push('sys_kore');
|
||||
defines.push('sys_g1');
|
||||
defines.push('sys_g2');
|
||||
defines.push('sys_g3');
|
||||
defines.push('sys_g4');
|
||||
defines.push('sys_a1');
|
||||
defines.push('sys_a2');
|
||||
|
||||
defines.push('kha_cpp');
|
||||
defines.push('kha_' + this.options.target);
|
||||
defines.push('kha_' + this.options.target + '_native');
|
||||
defines.push('kha_' + this.options.target + '_cpp');
|
||||
let graphics = this.options.graphics;
|
||||
if (graphics === GraphicsApi.Default) {
|
||||
graphics = defaults.graphicsApi(this.options.target);
|
||||
}
|
||||
defines.push('kha_' + graphics);
|
||||
defines.push('kha_kore');
|
||||
defines.push('kha_g1');
|
||||
defines.push('kha_g2');
|
||||
defines.push('kha_g3');
|
||||
defines.push('kha_g4');
|
||||
defines.push('kha_a1');
|
||||
defines.push('kha_a2');
|
||||
|
||||
if (this.options.vr === 'gearvr') {
|
||||
defines.push('vr_gearvr');
|
||||
}
|
||||
else if (this.options.vr === 'cardboard') {
|
||||
defines.push('vr_cardboard');
|
||||
}
|
||||
else if (this.options.vr === 'rift') {
|
||||
defines.push('vr_rift');
|
||||
}
|
||||
|
||||
if (this.options.raytrace === 'dxr') {
|
||||
defines.push('kha_dxr');
|
||||
}
|
||||
|
||||
return {
|
||||
from: this.options.from,
|
||||
to: path.join(this.sysdir() + '-build', 'Sources'),
|
||||
sources: this.sources,
|
||||
libraries: this.libraries,
|
||||
defines: defines,
|
||||
parameters: this.parameters,
|
||||
haxeDirectory: this.options.haxe,
|
||||
system: this.sysdir(),
|
||||
language: 'cpp',
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
name: name,
|
||||
main: this.options.main,
|
||||
};
|
||||
}
|
||||
|
||||
async export(name: string, targetOptions: any, haxeOptions: any): Promise<void> {
|
||||
|
||||
}
|
||||
|
||||
async copySound(platform: string, from: string, to: string, options: any) {
|
||||
if (options.quality < 1) {
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir(), path.dirname(to)));
|
||||
await convert(from, path.join(this.options.to, this.sysdir(), to + '.ogg'), this.options.ogg);
|
||||
return { files: [to + '.ogg'], sizes: [1] };
|
||||
}
|
||||
else {
|
||||
if (from.endsWith('.wav')) {
|
||||
fs.copySync(from.toString(), path.join(this.options.to, this.sysdir(), to + '.wav'), { overwrite: true });
|
||||
}
|
||||
else {
|
||||
throw 'Can not convert ' + from + ' to wav format.';
|
||||
}
|
||||
return { files: [to + '.wav'], sizes: [1] };
|
||||
}
|
||||
}
|
||||
|
||||
async copyImage(platform: string, from: string, to: string, options: any, cache: any) {
|
||||
if (platform === Platform.iOS && options.quality < 1) {
|
||||
let format = await exportImage(this.options.kha, this.options.kraffiti, from, path.join(this.options.to, this.sysdir(), to), options, 'pvr', true, false, cache);
|
||||
return { files: [to + '.' + format], sizes: [1] };
|
||||
}
|
||||
else if (platform === Platform.Windows && options.quality < 1 && (this.options.graphics === GraphicsApi.OpenGL || this.options.graphics === GraphicsApi.Vulkan)) {
|
||||
// let format = await exportImage(this.options.kha, from, path.join(this.options.to, this.sysdir(), to), options, 'ASTC', true, false, cache);
|
||||
let format = await exportImage(this.options.kha, this.options.kraffiti, from, path.join(this.options.to, this.sysdir(), to), options, 'DXT5', true, false, cache);
|
||||
return { files: [to + '.' + format], sizes: [1] };
|
||||
}
|
||||
else {
|
||||
let format = await exportImage(this.options.kha, this.options.kraffiti, from, path.join(this.options.to, this.sysdir(), to), options, 'lz4', true, false, cache);
|
||||
return { files: [to + '.' + format], sizes: [1] };
|
||||
}
|
||||
}
|
||||
|
||||
async copyBlob(platform: string, from: string, to: string) {
|
||||
fs.copySync(from.toString(), path.join(this.options.to, this.sysdir(), to), { overwrite: true });
|
||||
return { files: [to], sizes: [1] };
|
||||
}
|
||||
|
||||
async copyVideo(platform: string, from: string, to: string) {
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir(), path.dirname(to)));
|
||||
if (platform === Platform.Windows) {
|
||||
await convert(from, path.join(this.options.to, this.sysdir(), to + '.avi'), this.options.h264);
|
||||
return { files: [to + '.avi'], sizes: [1] };
|
||||
}
|
||||
else if (platform === Platform.iOS || platform === Platform.OSX) {
|
||||
await convert(from, path.join(this.options.to, this.sysdir(), to + '.mp4'), this.options.h264);
|
||||
return { files: [to + '.mp4'], sizes: [1] };
|
||||
}
|
||||
else if (platform === Platform.Android) {
|
||||
await convert(from, path.join(this.options.to, this.sysdir(), to + '.ts'), this.options.h264);
|
||||
return { files: [to + '.ts'], sizes: [1] };
|
||||
}
|
||||
else {
|
||||
await convert(from, path.join(this.options.to, this.sysdir(), to + '.ogv'), this.options.theora);
|
||||
return { files: [to + '.ogv'], sizes: [1] };
|
||||
}
|
||||
}
|
||||
}
|
136
Kha/Tools/khamake/src/Exporters/KincHLExporter.ts
Normal file
136
Kha/Tools/khamake/src/Exporters/KincHLExporter.ts
Normal file
@ -0,0 +1,136 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import * as defaults from '../defaults';
|
||||
import {KhaExporter} from './KhaExporter';
|
||||
import {convert} from '../Converter';
|
||||
import {executeHaxe} from '../Haxe';
|
||||
import {GraphicsApi} from '../GraphicsApi';
|
||||
import {Platform} from '../Platform';
|
||||
import {exportImage} from '../ImageTool';
|
||||
import {Options} from '../Options';
|
||||
import {Library} from '../Project';
|
||||
|
||||
export class KincHLExporter extends KhaExporter {
|
||||
constructor(options: Options) {
|
||||
super(options);
|
||||
// Files.removeDirectory(this.directory.resolve(Paths.get(this.sysdir() + "-build", "Sources")));
|
||||
}
|
||||
|
||||
backend(): string {
|
||||
return 'Kinc-HL';
|
||||
}
|
||||
|
||||
haxeOptions(name: string, targetOptions: any, defines: Array<string>) {
|
||||
defines.push('no-compilation');
|
||||
|
||||
defines.push('sys_' + this.options.target);
|
||||
defines.push('sys_g1');
|
||||
defines.push('sys_g2');
|
||||
defines.push('sys_g3');
|
||||
defines.push('sys_g4');
|
||||
defines.push('sys_a1');
|
||||
defines.push('sys_a2');
|
||||
|
||||
defines.push('kha_hl');
|
||||
defines.push('kha_' + this.options.target);
|
||||
defines.push('kha_' + this.options.target + '_hl');
|
||||
let graphics = this.options.graphics;
|
||||
if (graphics === GraphicsApi.Default) {
|
||||
graphics = defaults.graphicsApi(this.options.target);
|
||||
}
|
||||
defines.push('kha_' + graphics);
|
||||
defines.push('kha_g1');
|
||||
defines.push('kha_g2');
|
||||
defines.push('kha_g3');
|
||||
defines.push('kha_g4');
|
||||
defines.push('kha_a1');
|
||||
defines.push('kha_a2');
|
||||
|
||||
if (this.options.vr === 'gearvr') {
|
||||
defines.push('vr_gearvr');
|
||||
}
|
||||
else if (this.options.vr === 'cardboard') {
|
||||
defines.push('vr_cardboard');
|
||||
}
|
||||
else if (this.options.vr === 'rift') {
|
||||
defines.push('vr_rift');
|
||||
}
|
||||
|
||||
if (this.options.raytrace === 'dxr') {
|
||||
defines.push('kha_dxr');
|
||||
}
|
||||
|
||||
return {
|
||||
from: this.options.from,
|
||||
to: path.join(this.sysdir() + '-build', 'sources.c'),
|
||||
sources: this.sources,
|
||||
libraries: this.libraries,
|
||||
defines: defines,
|
||||
parameters: this.parameters,
|
||||
haxeDirectory: this.options.haxe,
|
||||
system: this.sysdir(),
|
||||
language: 'hl',
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
name: name,
|
||||
main: this.options.main,
|
||||
};
|
||||
}
|
||||
|
||||
async export(name: string, targetOptions: any, haxeOptions: any): Promise<void> {
|
||||
|
||||
}
|
||||
|
||||
async copySound(platform: string, from: string, to: string, options: any) {
|
||||
if (options.quality < 1) {
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir(), path.dirname(to)));
|
||||
let ogg = await convert(from, path.join(this.options.to, this.sysdir(), to + '.ogg'), this.options.ogg);
|
||||
return { files: [to + '.ogg'], sizes: [1] };
|
||||
}
|
||||
else {
|
||||
fs.copySync(from.toString(), path.join(this.options.to, this.sysdir(), to + '.wav'), { overwrite: true });
|
||||
return { files: [to + '.wav'], sizes: [1] };
|
||||
}
|
||||
}
|
||||
|
||||
async copyImage(platform: string, from: string, to: string, options: any, cache: any) {
|
||||
if (platform === Platform.iOS && options.quality < 1) {
|
||||
let format = await exportImage(this.options.kha, this.options.kraffiti, from, path.join(this.options.to, this.sysdir(), to), options, 'pvr', true, false, cache);
|
||||
return { files: [to + '.' + format], sizes: [1] };
|
||||
}
|
||||
else if (platform === Platform.Windows && options.quality < 1 && (this.options.graphics === GraphicsApi.OpenGL || this.options.graphics === GraphicsApi.Vulkan)) {
|
||||
// let format = await exportImage(this.options.kha, from, path.join(this.options.to, this.sysdir(), to), options, 'ASTC', true, false, cache);
|
||||
let format = await exportImage(this.options.kha, this.options.kraffiti, from, path.join(this.options.to, this.sysdir(), to), options, 'DXT5', true, false, cache);
|
||||
return { files: [to + '.' + format], sizes: [1] };
|
||||
}
|
||||
else {
|
||||
let format = await exportImage(this.options.kha, this.options.kraffiti, from, path.join(this.options.to, this.sysdir(), to), options, 'lz4', true, false, cache);
|
||||
return { files: [to + '.' + format], sizes: [1] };
|
||||
}
|
||||
}
|
||||
|
||||
async copyBlob(platform: string, from: string, to: string) {
|
||||
fs.copySync(from.toString(), path.join(this.options.to, this.sysdir(), to).toString(), { overwrite: true });
|
||||
return { files: [to], sizes: [1] };
|
||||
}
|
||||
|
||||
async copyVideo(platform: string, from: string, to: string) {
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir(), path.dirname(to)));
|
||||
if (platform === Platform.Windows) {
|
||||
await convert(from, path.join(this.options.to, this.sysdir(), to + '.avi'), this.options.h264);
|
||||
return { files: [to + '.avi'], sizes: [1] };
|
||||
}
|
||||
else if (platform === Platform.iOS || platform === Platform.OSX) {
|
||||
await convert(from, path.join(this.options.to, this.sysdir(), to + '.mp4'), this.options.h264);
|
||||
return { files: [to + '.mp4'], sizes: [1] };
|
||||
}
|
||||
else if (platform === Platform.Android) {
|
||||
await convert(from, path.join(this.options.to, this.sysdir(), to + '.ts'), this.options.h264);
|
||||
return { files: [to + '.ts'], sizes: [1] };
|
||||
}
|
||||
else {
|
||||
await convert(from, path.join(this.options.to, this.sysdir(), to + '.ogv'), this.options.theora);
|
||||
return { files: [to + '.ogv'], sizes: [1] };
|
||||
}
|
||||
}
|
||||
}
|
105
Kha/Tools/khamake/src/Exporters/KromExporter.ts
Normal file
105
Kha/Tools/khamake/src/Exporters/KromExporter.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import * as defaults from '../defaults';
|
||||
import {KhaExporter} from './KhaExporter';
|
||||
import {convert} from '../Converter';
|
||||
import {executeHaxe} from '../Haxe';
|
||||
import {GraphicsApi} from '../GraphicsApi';
|
||||
import {Options} from '../Options';
|
||||
import {exportImage} from '../ImageTool';
|
||||
import {Library} from '../Project';
|
||||
|
||||
export class KromExporter extends KhaExporter {
|
||||
width: number;
|
||||
height: number;
|
||||
|
||||
constructor(options: Options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
backend(): string {
|
||||
return 'Krom';
|
||||
}
|
||||
|
||||
haxeOptions(name: string, targetOptions: any, defines: Array<string>) {
|
||||
defines.push('sys_' + this.options.target);
|
||||
defines.push('sys_g1');
|
||||
defines.push('sys_g2');
|
||||
defines.push('sys_g3');
|
||||
defines.push('sys_g4');
|
||||
defines.push('sys_a1');
|
||||
defines.push('sys_a2');
|
||||
|
||||
defines.push('kha_js');
|
||||
defines.push('kha_' + this.options.target);
|
||||
defines.push('kha_' + this.options.target + '_js');
|
||||
let graphics = this.options.graphics;
|
||||
if (graphics === GraphicsApi.Default) {
|
||||
graphics = defaults.graphicsApi(this.options.target);
|
||||
}
|
||||
defines.push('kha_' + graphics);
|
||||
defines.push('kha_g1');
|
||||
defines.push('kha_g2');
|
||||
defines.push('kha_g3');
|
||||
defines.push('kha_g4');
|
||||
defines.push('kha_a1');
|
||||
defines.push('kha_a2');
|
||||
|
||||
if (this.options.debug) {
|
||||
this.parameters.push('-debug');
|
||||
defines.push('js-classic');
|
||||
}
|
||||
|
||||
return {
|
||||
from: this.options.from.toString(),
|
||||
to: path.join(this.sysdir(), 'krom.js.temp'),
|
||||
realto: path.join(this.sysdir(), 'krom.js'),
|
||||
sources: this.sources,
|
||||
libraries: this.libraries,
|
||||
defines: defines,
|
||||
parameters: this.parameters,
|
||||
haxeDirectory: this.options.haxe,
|
||||
system: this.sysdir(),
|
||||
language: 'js',
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
name: name,
|
||||
main: this.options.main,
|
||||
};
|
||||
}
|
||||
|
||||
async export(name: string, targetOptions: any, haxeOptions: any): Promise<void> {
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir()));
|
||||
}
|
||||
|
||||
async copySound(platform: string, from: string, to: string, options: any) {
|
||||
if (options.quality < 1) {
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir(), path.dirname(to)));
|
||||
let ogg = await convert(from, path.join(this.options.to, this.sysdir(), to + '.ogg'), this.options.ogg);
|
||||
return { files: [to + '.ogg'], sizes: [1] };
|
||||
}
|
||||
else {
|
||||
fs.copySync(from.toString(), path.join(this.options.to, this.sysdir(), to + '.wav'), { overwrite: true });
|
||||
return { files: [to + '.wav'], sizes: [1] };
|
||||
}
|
||||
}
|
||||
|
||||
async copyImage(platform: string, from: string, to: string, options: any, cache: any) {
|
||||
let format = await exportImage(this.options.kha, this.options.kraffiti, from, path.join(this.options.to, this.sysdir(), to), options, undefined, false, false, cache);
|
||||
return { files: [to + '.' + format], sizes: [1] };
|
||||
}
|
||||
|
||||
async copyBlob(platform: string, from: string, to: string) {
|
||||
fs.copySync(from.toString(), path.join(this.options.to, this.sysdir(), to), { overwrite: true });
|
||||
return { files: [to], sizes: [1] };
|
||||
}
|
||||
|
||||
async copyVideo(platform: string, from: string, to: string) {
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir(), path.dirname(to)));
|
||||
let webm = await convert(from, path.join(this.options.to, this.sysdir(), to + '.webm'), this.options.webm);
|
||||
let files: string[] = [];
|
||||
let sizes: number[] = [];
|
||||
if (webm) { files.push(to + '.webm'); sizes.push(1); }
|
||||
return { files: files, sizes: sizes };
|
||||
}
|
||||
}
|
17
Kha/Tools/khamake/src/Exporters/NodeExporter.ts
Normal file
17
Kha/Tools/khamake/src/Exporters/NodeExporter.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import * as path from 'path';
|
||||
import {Html5Exporter} from './Html5Exporter';
|
||||
import {Options} from '../Options';
|
||||
|
||||
export class NodeExporter extends Html5Exporter {
|
||||
constructor(options: Options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
backend(): string {
|
||||
return 'Node';
|
||||
}
|
||||
|
||||
isNode() {
|
||||
return true;
|
||||
}
|
||||
}
|
165
Kha/Tools/khamake/src/Exporters/PlayStationMobileExporter.ts
Normal file
165
Kha/Tools/khamake/src/Exporters/PlayStationMobileExporter.ts
Normal file
@ -0,0 +1,165 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import {CSharpExporter} from './CSharpExporter';
|
||||
import {convert} from '../Converter';
|
||||
import {executeHaxe} from '../Haxe';
|
||||
import {Options} from '../Options';
|
||||
import {exportImage} from '../ImageTool';
|
||||
const uuid = require('uuid');
|
||||
|
||||
export class PlayStationMobileExporter extends CSharpExporter {
|
||||
files: Array<string>;
|
||||
|
||||
constructor(options: Options) {
|
||||
super(options);
|
||||
this.files = [];
|
||||
}
|
||||
|
||||
backend() {
|
||||
return 'PSM';
|
||||
}
|
||||
|
||||
exportSLN(projectUuid: string) {
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir() + '-build'));
|
||||
this.writeFile(path.join(this.options.to, this.sysdir() + '-build', 'Project.sln'));
|
||||
const solutionUuid = uuid.v4();
|
||||
|
||||
this.p('Microsoft Visual Studio Solution File, Format Version 11.00');
|
||||
this.p('# Visual Studio 2010');
|
||||
this.p('Project("{' + solutionUuid.toString().toUpperCase() + '}") = "HaxeProject", "Project.csproj", "{' + projectUuid.toString().toUpperCase() + '}"');
|
||||
this.p('EndProject');
|
||||
this.p('Global');
|
||||
this.p('GlobalSection(SolutionConfigurationPlatforms) = preSolution', 1);
|
||||
this.p('Debug|Any CPU = Debug|Any CPU', 2);
|
||||
this.p('Release|Any CPU = Release|Any CPU', 2);
|
||||
this.p('EndGlobalSection', 1);
|
||||
this.p('GlobalSection(ProjectConfigurationPlatforms) = postSolution', 1);
|
||||
this.p('{" + projectUuid.toString().toUpperCase() + "}.Debug|Any CPU.ActiveCfg = Debug|Any CPU', 2);
|
||||
this.p('{" + projectUuid.toString().toUpperCase() + "}.Debug|Any CPU.Build.0 = Debug|Any CPU', 2);
|
||||
this.p('{" + projectUuid.toString().toUpperCase() + "}.Release|Any CPU.ActiveCfg = Release|Any CPU', 2);
|
||||
this.p('{" + projectUuid.toString().toUpperCase() + "}.Release|Any CPU.Build.0 = Release|Any CPU', 2);
|
||||
this.p('EndGlobalSection', 1);
|
||||
this.p('GlobalSection(MonoDevelopProperties) = preSolution', 1);
|
||||
this.p('StartupItem = Project.csproj', 2);
|
||||
this.p('EndGlobalSection', 1);
|
||||
this.p('EndGlobal');
|
||||
this.closeFile();
|
||||
}
|
||||
|
||||
exportResources() {
|
||||
this.createDirectory(path.join(this.options.to, this.sysdir() + '-build', 'shaders'));
|
||||
fs.writeFileSync(path.join(this.options.to, this.sysdir() + '-build', 'shaders', 'Simple.fcg'),
|
||||
'void main(float4 out Color : COLOR, uniform float4 MaterialColor) {\n'
|
||||
+ '\tColor = MaterialColor;\n'
|
||||
+ '}\n');
|
||||
|
||||
fs.writeFileSync(path.join(this.options.to, this.sysdir() + '-build', 'shaders', 'Simple.vcg'),
|
||||
'void main(float4 in a_Position : POSITION, float4 out v_Position : POSITION, uniform float4x4 WorldViewProj) {\n'
|
||||
+ '\tv_Position = mul(a_Position, WorldViewProj);\n'
|
||||
+ '}\n');
|
||||
|
||||
fs.writeFileSync(path.join(this.options.to, this.sysdir() + '-build', 'shaders', 'Texture.fcg'),
|
||||
'void main(float2 in v_TexCoord : TEXCOORD0, float4 out Color : COLOR, uniform sampler2D Texture0 : TEXUNIT0) {\n'
|
||||
+ '\tColor = tex2D(Texture0, v_TexCoord);\n'
|
||||
+ '}\n');
|
||||
|
||||
fs.writeFileSync(path.join(this.options.to, this.sysdir() + '-build', 'shaders', 'Texture.vcg'),
|
||||
'void main(float4 in a_Position : POSITION, float2 in a_TexCoord : TEXCOORD0, float4 out v_Position : POSITION, float2 out v_TexCoord : TEXCOORD0, uniform float4x4 WorldViewProj) {\n'
|
||||
+ '\tv_Position = mul(a_Position, WorldViewProj);\n'
|
||||
+ '\tv_TexCoord = a_TexCoord;\n'
|
||||
+ '}\n');
|
||||
|
||||
let appxml = path.join(this.options.to, this.sysdir() + '-build', 'app.xml');
|
||||
if (!fs.existsSync(appxml)) {
|
||||
let appxmltext = fs.readFileSync(path.join(__dirname, 'Data', 'psm', 'app.xml'), {encoding: 'utf8'});
|
||||
fs.writeFileSync(appxml.toString(), appxmltext);
|
||||
}
|
||||
}
|
||||
|
||||
exportCsProj(projectUuid: string) {
|
||||
this.writeFile(path.join(this.options.to, this.sysdir() + '-build', 'Project.csproj'));
|
||||
this.p('<?xml version="1.0" encoding="utf-8"?>');
|
||||
this.p('<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">');
|
||||
this.p('<PropertyGroup>', 1);
|
||||
this.p('<Configuration Condition=" \'$(Configuration)\' == \'\' ">Debug</Configuration>', 2);
|
||||
this.p('<Platform Condition=" \'$(Platform)\' == \'\' ">AnyCPU</Platform>', 2);
|
||||
this.p('<ProductVersion>10.0.0</ProductVersion>', 2);
|
||||
this.p('<SchemaVersion>2.0</SchemaVersion>', 2);
|
||||
this.p('<ProjectGuid>{' + projectUuid.toString().toUpperCase() + '}</ProjectGuid>', 2);
|
||||
this.p('<ProjectTypeGuids>{69878862-DA7D-4DC6-B0A1-50D8FAB4242F};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>', 2);
|
||||
this.p('<OutputType>Exe</OutputType>', 2);
|
||||
this.p('<RootNamespace>PSTest</RootNamespace>', 2);
|
||||
this.p('<AssemblyName>PSTest</AssemblyName>', 2);
|
||||
this.p('</PropertyGroup>', 1);
|
||||
this.p('<PropertyGroup Condition=" \'$(Configuration)|$(Platform)\' == \'Debug|AnyCPU\' ">', 1);
|
||||
this.p('<DebugSymbols>true</DebugSymbols>', 2);
|
||||
this.p('<DebugType>full</DebugType>', 2);
|
||||
this.p('<Optimize>false</Optimize>', 2);
|
||||
this.p('<OutputPath>bin\\Debug</OutputPath>', 2);
|
||||
this.p('<DefineConstants>DEBUG;</DefineConstants>', 2);
|
||||
this.p('<ErrorReport>prompt</ErrorReport>', 2);
|
||||
this.p('<WarningLevel>4</WarningLevel>', 2);
|
||||
this.p('<ConsolePause>false</ConsolePause>', 2);
|
||||
this.p('</PropertyGroup>', 1);
|
||||
this.p('<PropertyGroup Condition=" \'$(Configuration)|$(Platform)\' == \'Release|AnyCPU\' ">', 1);
|
||||
this.p('<DebugType>none</DebugType>', 2);
|
||||
this.p('<Optimize>true</Optimize>', 2);
|
||||
this.p('<OutputPath>bin\\Release</OutputPath>', 2);
|
||||
this.p('<ErrorReport>prompt</ErrorReport>', 2);
|
||||
this.p('<WarningLevel>4</WarningLevel>', 2);
|
||||
this.p('<ConsolePause>false</ConsolePause>', 2);
|
||||
this.p('</PropertyGroup>', 1);
|
||||
this.p('<ItemGroup>', 1);
|
||||
this.p('<Reference Include="System" />', 2);
|
||||
this.p('<Reference Include="System.Xml" />', 2);
|
||||
this.p('<Reference Include="System.Core" />', 2);
|
||||
this.p('<Reference Include="Sce.PlayStation.Core" />', 2);
|
||||
this.p('</ItemGroup>', 1);
|
||||
this.p('<ItemGroup>', 1);
|
||||
this.includeFiles(path.join(this.options.to, this.sysdir() + '-build', 'Sources', 'src'), path.join(this.options.to, this.sysdir() + '-build'));
|
||||
this.p('</ItemGroup>', 1);
|
||||
this.p('<ItemGroup>', 1);
|
||||
this.p('<ShaderProgram Include="shaders\\Simple.fcg" />', 2);
|
||||
this.p('<ShaderProgram Include="shaders\\Simple.vcg" />', 2);
|
||||
this.p('<ShaderProgram Include="shaders\\Texture.fcg" />', 2);
|
||||
this.p('<ShaderProgram Include="shaders\\Texture.vcg" />', 2);
|
||||
this.p('</ItemGroup>', 1);
|
||||
this.p('<ItemGroup>', 1);
|
||||
this.p('<Folder Include="resources\\" />', 2);
|
||||
this.p('</ItemGroup>', 1);
|
||||
this.p('<ItemGroup>', 1);
|
||||
for (let file of this.files) {
|
||||
this.p('<Content Include="..\\' + this.sysdir() + '\\' + file.toString() + '">', 2);
|
||||
this.p('<Link>resources\\' + file.toString() + '</Link>', 3);
|
||||
this.p('</Content>', 2);
|
||||
}
|
||||
this.p('</ItemGroup>', 1);
|
||||
this.p('<Import Project="$(MSBuildExtensionsPath)\\Sce\\Sce.Psm.CSharp.targets" />', 1);
|
||||
this.p('</Project>');
|
||||
this.closeFile();
|
||||
}
|
||||
|
||||
/*copyMusic(platform, from, to, encoders, callback) {
|
||||
callback();
|
||||
}*/
|
||||
|
||||
async copySound(platform: string, from: string, to: string) {
|
||||
return { files: [''], sizes: [1] };
|
||||
}
|
||||
|
||||
async copyImage(platform: string, from: string, to: string, asset: any, cache: any) {
|
||||
this.files.push(asset['file']);
|
||||
let format = await exportImage(this.options.kha, this.options.kraffiti, from, path.join(this.options.to, this.sysdir(), to), asset, undefined, false, false, cache);
|
||||
return { files: [to + '.' + format], sizes: [1] };
|
||||
}
|
||||
|
||||
async copyBlob(platform: string, from: string, to: string) {
|
||||
fs.copySync(from.toString(), path.join(this.options.to, this.sysdir(), to), { overwrite: true });
|
||||
this.files.push(to);
|
||||
return { files: [to], sizes: [1] };
|
||||
}
|
||||
|
||||
async copyVideo(platform: string, from: string, to: string) {
|
||||
return { files: [''], sizes: [1] };
|
||||
}
|
||||
}
|
287
Kha/Tools/khamake/src/Exporters/WpfExporter.ts
Normal file
287
Kha/Tools/khamake/src/Exporters/WpfExporter.ts
Normal file
@ -0,0 +1,287 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import {CSharpExporter} from './CSharpExporter';
|
||||
import {convert} from '../Converter';
|
||||
import {executeHaxe} from '../Haxe';
|
||||
import {Options} from '../Options';
|
||||
import {exportImage} from '../ImageTool';
|
||||
const uuid = require('uuid');
|
||||
|
||||
export class WpfExporter extends CSharpExporter {
|
||||
constructor(options: Options) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
backend() {
|
||||
return 'WPF';
|
||||
}
|
||||
|
||||
exportResources() {
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir() + '-build', 'Properties'));
|
||||
this.writeFile(path.join(this.options.to, this.sysdir() + '-build', 'Properties', 'AssemblyInfo.cs'));
|
||||
this.p('using System.Reflection;');
|
||||
this.p('using System.Resources;');
|
||||
this.p('using System.Runtime.CompilerServices;');
|
||||
this.p('using System.Runtime.InteropServices;');
|
||||
this.p('using System.Windows;');
|
||||
this.p();
|
||||
this.p('[assembly: AssemblyTitle("HaxeProject")]');
|
||||
this.p('[assembly: AssemblyDescription("")]');
|
||||
this.p('[assembly: AssemblyConfiguration("")]');
|
||||
this.p('[assembly: AssemblyCompany("Kha Development Team")]');
|
||||
this.p('[assembly: AssemblyProduct("HaxeProject")]');
|
||||
this.p('[assembly: AssemblyCopyright("Copyright ? Kha Development Team 2018")]');
|
||||
this.p('[assembly: AssemblyTrademark("")]');
|
||||
this.p('[assembly: AssemblyCulture("")]');
|
||||
this.p();
|
||||
this.p('[assembly: ComVisible(false)]');
|
||||
this.p();
|
||||
this.p('//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]');
|
||||
this.p();
|
||||
this.p('[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]');
|
||||
this.p();
|
||||
this.p('// [assembly: AssemblyVersion("1.0.*")]');
|
||||
this.p('[assembly: AssemblyVersion("1.0.0.0")]');
|
||||
this.p('[assembly: AssemblyFileVersion("1.0.0.0")]');
|
||||
this.closeFile();
|
||||
|
||||
this.writeFile(path.join(this.options.to, this.sysdir() + '-build', 'Properties', 'Resources.Designer.cs'));
|
||||
this.p('namespace WpfApplication1.Properties {');
|
||||
this.p('[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]', 1);
|
||||
this.p('[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]', 1);
|
||||
this.p('[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]', 1);
|
||||
this.p('internal class Resources', 1);
|
||||
this.p('{', 1);
|
||||
this.p('private static global::System.Resources.ResourceManager resourceMan;', 2);
|
||||
this.p('private static global::System.Globalization.CultureInfo resourceCulture;', 2);
|
||||
this.p();
|
||||
this.p('[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]', 2);
|
||||
this.p('internal Resources() { }', 2);
|
||||
this.p();
|
||||
this.p('[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]', 2);
|
||||
this.p('internal static global::System.Resources.ResourceManager ResourceManager', 2);
|
||||
this.p('{', 2);
|
||||
this.p('get', 3);
|
||||
this.p('{', 3);
|
||||
this.p('if ((resourceMan == null))', 4);
|
||||
this.p('{', 4);
|
||||
this.p('global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WpfApplication1.Properties.Resources", typeof(Resources).Assembly);', 5);
|
||||
this.p('resourceMan = temp;', 5);
|
||||
this.p('}', 4);
|
||||
this.p('return resourceMan;', 4);
|
||||
this.p('}', 3);
|
||||
this.p('}', 2);
|
||||
this.p();
|
||||
this.p('[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]', 2);
|
||||
this.p('internal static global::System.Globalization.CultureInfo Culture', 2);
|
||||
this.p('{', 2);
|
||||
this.p('get', 3);
|
||||
this.p('{', 3);
|
||||
this.p('return resourceCulture;', 4);
|
||||
this.p('}', 3);
|
||||
this.p('set', 3);
|
||||
this.p('{', 3);
|
||||
this.p('resourceCulture = value;', 4);
|
||||
this.p('}', 3);
|
||||
this.p('}', 2);
|
||||
this.p('}', 1);
|
||||
this.p('}');
|
||||
this.closeFile();
|
||||
|
||||
this.writeFile(path.join(this.options.to, this.sysdir() + '-build', 'Properties', 'Resources.resx'));
|
||||
this.p('<?xml version="1.0" encoding="utf-8"?>');
|
||||
this.p('<root>');
|
||||
this.p('<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">');
|
||||
this.p('<xsd:element name="root" msdata:IsDataSet="true">');
|
||||
this.p('<xsd:complexType>');
|
||||
this.p('<xsd:choice maxOccurs="unbounded">');
|
||||
this.p('<xsd:element name="metadata">');
|
||||
this.p('<xsd:complexType>');
|
||||
this.p('<xsd:sequence>');
|
||||
this.p('<xsd:element name="value" type="xsd:string" minOccurs="0" />');
|
||||
this.p('</xsd:sequence>');
|
||||
this.p('<xsd:attribute name="name" type="xsd:string" />');
|
||||
this.p('<xsd:attribute name="type" type="xsd:string" />');
|
||||
this.p('<xsd:attribute name="mimetype" type="xsd:string" />');
|
||||
this.p('</xsd:complexType>');
|
||||
this.p('</xsd:element>');
|
||||
this.p('<xsd:element name="assembly">');
|
||||
this.p('<xsd:complexType>');
|
||||
this.p('<xsd:attribute name="alias" type="xsd:string" />');
|
||||
this.p('<xsd:attribute name="name" type="xsd:string" />');
|
||||
this.p('</xsd:complexType>');
|
||||
this.p('</xsd:element>');
|
||||
this.p('<xsd:element name="data">');
|
||||
this.p('<xsd:complexType>');
|
||||
this.p('<xsd:sequence>');
|
||||
this.p('<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />');
|
||||
this.p('<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />');
|
||||
this.p('</xsd:sequence>');
|
||||
this.p('<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />');
|
||||
this.p('<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />');
|
||||
this.p('<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />');
|
||||
this.p('</xsd:complexType>');
|
||||
this.p('</xsd:element>');
|
||||
this.p('<xsd:element name="resheader">');
|
||||
this.p('<xsd:complexType>');
|
||||
this.p('<xsd:sequence>');
|
||||
this.p('<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />');
|
||||
this.p('</xsd:sequence>');
|
||||
this.p('<xsd:attribute name="name" type="xsd:string" use="required" />');
|
||||
this.p('</xsd:complexType>');
|
||||
this.p('</xsd:element>');
|
||||
this.p('</xsd:choice>');
|
||||
this.p('</xsd:complexType>');
|
||||
this.p('</xsd:element>');
|
||||
this.p('</xsd:schema>');
|
||||
this.p('<resheader name="resmimetype">');
|
||||
this.p('<value>text/microsoft-resx</value>');
|
||||
this.p('</resheader>');
|
||||
this.p('<resheader name="version">');
|
||||
this.p('<value>2.0</value>');
|
||||
this.p('</resheader>');
|
||||
this.p('<resheader name="reader">');
|
||||
this.p('<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>');
|
||||
this.p('</resheader>');
|
||||
this.p('<resheader name="writer">');
|
||||
this.p('<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>');
|
||||
this.p('</resheader>');
|
||||
this.p('</root>');
|
||||
this.closeFile();
|
||||
|
||||
this.writeFile(path.join(this.options.to, this.sysdir() + '-build', 'Properties', 'Settings.Designer.cs'));
|
||||
this.p('namespace WpfApplication1.Properties');
|
||||
this.p('{');
|
||||
this.p('[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]', 1);
|
||||
this.p('[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]', 1);
|
||||
this.p('internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase', 1);
|
||||
this.p('{', 1);
|
||||
this.p('private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));', 2);
|
||||
this.p();
|
||||
this.p('public static Settings Default', 2);
|
||||
this.p('{', 2);
|
||||
this.p('get', 3);
|
||||
this.p('{', 3);
|
||||
this.p('return defaultInstance;', 4);
|
||||
this.p('}', 3);
|
||||
this.p('}', 2);
|
||||
this.p('}', 1);
|
||||
this.p('}');
|
||||
this.closeFile();
|
||||
|
||||
this.writeFile(path.join(this.options.to, this.sysdir() + '-build', 'Properties', 'Settings.settings'));
|
||||
this.p('<?xml version="1.0" encoding="utf-8"?>');
|
||||
this.p('<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">');
|
||||
this.p('<Profiles>');
|
||||
this.p('<Profile Name="(Default)" />');
|
||||
this.p('</Profiles>');
|
||||
this.p('<Settings />');
|
||||
this.p('</SettingsFile>');
|
||||
|
||||
this.closeFile();
|
||||
}
|
||||
|
||||
exportCsProj(projectUuid: string) {
|
||||
this.writeFile(path.join(this.options.to, this.sysdir() + '-build', 'Project.csproj'));
|
||||
this.p('<?xml version="1.0" encoding="utf-8"?>');
|
||||
this.p('<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">');
|
||||
this.p('<PropertyGroup>', 1);
|
||||
this.p('<Configuration Condition=" \'$(Configuration)\' == \'\' ">Debug</Configuration>', 2);
|
||||
this.p('<Platform Condition=" \'$(Platform)\' == \'\' ">x86</Platform>', 2);
|
||||
this.p('<ProductVersion>8.0.30703</ProductVersion>', 2);
|
||||
this.p('<SchemaVersion>2.0</SchemaVersion>', 2);
|
||||
this.p('<ProjectGuid>{' + projectUuid.toString().toUpperCase() + '}</ProjectGuid>', 2);
|
||||
this.p('<OutputType>Library</OutputType>', 2);
|
||||
this.p('<AppDesignerFolder>Properties</AppDesignerFolder>', 2);
|
||||
this.p('<RootNamespace>WpfApplication1</RootNamespace>', 2);
|
||||
this.p('<AssemblyName>WpfApplication1</AssemblyName>', 2);
|
||||
this.p('<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>', 2);
|
||||
this.p('<TargetFrameworkProfile>Client</TargetFrameworkProfile>', 2);
|
||||
this.p('<FileAlignment>512</FileAlignment>', 2);
|
||||
this.p('<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>', 2);
|
||||
this.p('<WarningLevel>4</WarningLevel>', 2);
|
||||
this.p('</PropertyGroup>', 1);
|
||||
this.p('<PropertyGroup Condition=" \'$(Configuration)|$(Platform)\' == \'Debug|x86\' ">', 1);
|
||||
this.p('<PlatformTarget>x86</PlatformTarget>', 2);
|
||||
this.p('<DebugSymbols>true</DebugSymbols>', 2);
|
||||
this.p('<DebugType>full</DebugType>', 2);
|
||||
this.p('<Optimize>false</Optimize>', 2);
|
||||
this.p('<OutputPath>bin\\Debug\\</OutputPath>', 2);
|
||||
this.p('<DefineConstants>DEBUG;TRACE</DefineConstants>', 2);
|
||||
this.p('<ErrorReport>prompt</ErrorReport>', 2);
|
||||
this.p('<WarningLevel>4</WarningLevel>', 2);
|
||||
this.p('</PropertyGroup>', 1);
|
||||
this.p('<PropertyGroup Condition=" \'$(Configuration)|$(Platform)\' == \'Release|x86\' ">', 1);
|
||||
this.p('<PlatformTarget>x86</PlatformTarget>', 2);
|
||||
this.p('<DebugType>pdbonly</DebugType>', 2);
|
||||
this.p('<Optimize>true</Optimize>', 2);
|
||||
this.p('<OutputPath>bin\\Release\\</OutputPath>', 2);
|
||||
this.p('<DefineConstants>TRACE</DefineConstants>', 2);
|
||||
this.p('<ErrorReport>prompt</ErrorReport>', 2);
|
||||
this.p('<WarningLevel>4</WarningLevel>', 2);
|
||||
this.p('</PropertyGroup>', 1);
|
||||
this.p('<ItemGroup>', 1);
|
||||
this.p('<Reference Include="System" />', 2);
|
||||
this.p('<Reference Include="System.Data" />', 2);
|
||||
this.p('<Reference Include="System.Xml" />', 2);
|
||||
this.p('<Reference Include="Microsoft.CSharp" />', 2);
|
||||
this.p('<Reference Include="System.Core" />', 2);
|
||||
this.p('<Reference Include="System.Xml.Linq" />', 2);
|
||||
this.p('<Reference Include="System.Data.DataSetExtensions" />', 2);
|
||||
this.p('<Reference Include="System.Xaml">', 2);
|
||||
this.p('<RequiredTargetFramework>4.0</RequiredTargetFramework>', 3);
|
||||
this.p('</Reference>', 2);
|
||||
this.p('<Reference Include="WindowsBase" />', 2);
|
||||
this.p('<Reference Include="PresentationCore" />', 2);
|
||||
this.p('<Reference Include="PresentationFramework" />', 2);
|
||||
this.p('</ItemGroup>', 1);
|
||||
this.p('<ItemGroup>', 1);
|
||||
this.includeFiles(path.join(this.options.to, this.sysdir() + '-build', 'Sources', 'src'), path.join(this.options.to, this.sysdir() + '-build'));
|
||||
this.p('</ItemGroup>', 1);
|
||||
this.p('<ItemGroup>', 1);
|
||||
this.p('<Compile Include="Properties\\AssemblyInfo.cs">', 2);
|
||||
this.p('<SubType>Code</SubType>', 3);
|
||||
this.p('</Compile>', 2);
|
||||
this.p('<Compile Include="Properties\\Resources.Designer.cs">', 2);
|
||||
this.p('<AutoGen>True</AutoGen>', 3);
|
||||
this.p('<DesignTime>True</DesignTime>', 3);
|
||||
this.p('<DependentUpon>Resources.resx</DependentUpon>', 3);
|
||||
this.p('</Compile>', 2);
|
||||
this.p('<Compile Include="Properties\\Settings.Designer.cs">', 2);
|
||||
this.p('<AutoGen>True</AutoGen>', 3);
|
||||
this.p('<DependentUpon>Settings.settings</DependentUpon>', 3);
|
||||
this.p('<DesignTimeSharedInput>True</DesignTimeSharedInput>', 3);
|
||||
this.p('</Compile>', 2);
|
||||
this.p('<EmbeddedResource Include="Properties\\Resources.resx">', 2);
|
||||
this.p('<Generator>ResXFileCodeGenerator</Generator>', 3);
|
||||
this.p('<LastGenOutput>Resources.Designer.cs</LastGenOutput>', 3);
|
||||
this.p('</EmbeddedResource>', 2);
|
||||
this.p('<None Include="Properties\\Settings.settings">', 2);
|
||||
this.p('<Generator>SettingsSingleFileGenerator</Generator>', 3);
|
||||
this.p('<LastGenOutput>Settings.Designer.cs</LastGenOutput>', 3);
|
||||
this.p('</None>', 2);
|
||||
this.p('<AppDesigner Include="Properties\\" />', 2);
|
||||
this.p('</ItemGroup>', 1);
|
||||
this.p('<Import Project="$(MSBuildToolsPath)\\Microsoft.CSharp.targets" />', 1);
|
||||
this.p('</Project>');
|
||||
this.closeFile();
|
||||
}
|
||||
|
||||
/*copyMusic(platform, from, to, encoders, callback) {
|
||||
Files.createDirectories(this.directory.resolve(this.sysdir()).resolve(to).parent());
|
||||
Converter.convert(from, this.directory.resolve(this.sysdir()).resolve(to + '.mp4'), encoders.aacEncoder, () => {
|
||||
callback([to + '.mp4']);
|
||||
});
|
||||
}*/
|
||||
|
||||
async copySound(platform: string, from: string, to: string) {
|
||||
fs.copySync(from.toString(), path.join(this.options.to, this.sysdir(), to + '.wav'), { overwrite: true });
|
||||
return { files: [to + '.wav'], sizes: [1] };
|
||||
}
|
||||
|
||||
async copyVideo(platform: string, from: string, to: string) {
|
||||
fs.ensureDirSync(path.join(this.options.to, this.sysdir(), path.dirname(to)));
|
||||
await convert(from, path.join(this.options.to, this.sysdir(), to + '.wmv'), this.options.wmv);
|
||||
return { files: [to + '.wmv'], sizes: [1] };
|
||||
}
|
||||
}
|
10
Kha/Tools/khamake/src/GraphicsApi.ts
Normal file
10
Kha/Tools/khamake/src/GraphicsApi.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export let GraphicsApi = {
|
||||
Default: 'default',
|
||||
OpenGL: 'opengl',
|
||||
OpenGL1: 'opengl1',
|
||||
Direct3D9: 'direct3d9',
|
||||
Direct3D11: 'direct3d11',
|
||||
Direct3D12: 'direct3d12',
|
||||
Metal: 'metal',
|
||||
Vulkan: 'vulkan'
|
||||
};
|
37
Kha/Tools/khamake/src/Haxe.ts
Normal file
37
Kha/Tools/khamake/src/Haxe.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import * as child_process from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as log from './log';
|
||||
import {sys} from './exec';
|
||||
|
||||
export function executeHaxe(from: string, haxeDirectory: string, options: string[]): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let exe = 'haxe';
|
||||
let env = process.env;
|
||||
if (fs.existsSync(haxeDirectory) && fs.statSync(haxeDirectory).isDirectory()) {
|
||||
let localexe = path.resolve(haxeDirectory, 'haxe' + sys());
|
||||
if (!fs.existsSync(localexe)) localexe = path.resolve(haxeDirectory, 'haxe');
|
||||
if (fs.existsSync(localexe)) exe = localexe;
|
||||
const stddir = path.resolve(haxeDirectory, 'std');
|
||||
if (fs.existsSync(stddir) && fs.statSync(stddir).isDirectory()) {
|
||||
env.HAXE_STD_PATH = stddir;
|
||||
}
|
||||
}
|
||||
let haxe = child_process.spawn(exe, options, {env: env, cwd: path.normalize(from)});
|
||||
|
||||
haxe.stdout.on('data', (data: any) => {
|
||||
log.info(data.toString());
|
||||
});
|
||||
|
||||
haxe.stderr.on('data', (data: any) => {
|
||||
log.error(data.toString());
|
||||
});
|
||||
|
||||
haxe.on('close', (code: number) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
}
|
||||
else reject('Haxe compiler error.');
|
||||
});
|
||||
});
|
||||
}
|
250
Kha/Tools/khamake/src/HaxeCompiler.ts
Normal file
250
Kha/Tools/khamake/src/HaxeCompiler.ts
Normal file
@ -0,0 +1,250 @@
|
||||
import {Callbacks} from './ProjectFile';
|
||||
import * as child_process from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as chokidar from 'chokidar';
|
||||
import * as log from './log';
|
||||
import {sys} from './exec';
|
||||
import { WebSocketServer, WebSocket } from 'ws';
|
||||
|
||||
export class HaxeCompiler {
|
||||
from: string;
|
||||
haxeDirectory: string;
|
||||
hxml: string;
|
||||
sourceMatchers: Array<string>;
|
||||
watcher: fs.FSWatcher;
|
||||
ready: boolean = true;
|
||||
todo: boolean = false;
|
||||
port: string = '7000';
|
||||
isLiveReload: boolean = false;
|
||||
wss: WebSocketServer;
|
||||
wsClients: Array<WebSocket> = [];
|
||||
temp: string;
|
||||
to: string;
|
||||
resourceDir: string;
|
||||
compilationServer: child_process.ChildProcess;
|
||||
sysdir: string;
|
||||
|
||||
constructor(from: string, temp: string, to: string, resourceDir: string, haxeDirectory: string, hxml: string, sourceDirectories: Array<string>, sysdir: string, port: string, isLiveReload: boolean, httpPort: string) {
|
||||
this.from = from;
|
||||
this.temp = temp;
|
||||
this.to = to;
|
||||
this.resourceDir = resourceDir;
|
||||
this.haxeDirectory = haxeDirectory;
|
||||
this.hxml = hxml;
|
||||
this.sysdir = sysdir;
|
||||
this.port = port;
|
||||
this.isLiveReload = isLiveReload;
|
||||
|
||||
this.sourceMatchers = [];
|
||||
for (let dir of sourceDirectories) {
|
||||
this.sourceMatchers.push(path.join(dir, '**').replace(/\\/g, '/'));
|
||||
}
|
||||
|
||||
if (isLiveReload) {
|
||||
this.wss = new WebSocketServer({
|
||||
port: parseInt(httpPort) + 1
|
||||
});
|
||||
this.wss.on('connection', (client) => {
|
||||
if (this.wsClients.includes(client)) return;
|
||||
this.wsClients.push(client);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
close(): void {
|
||||
if (this.watcher) this.watcher.close();
|
||||
if (this.compilationServer) this.compilationServer.kill();
|
||||
if (this.isLiveReload) this.wss.close();
|
||||
}
|
||||
|
||||
async run(watch: boolean) {
|
||||
if (watch) {
|
||||
this.watcher = chokidar.watch(this.sourceMatchers, { ignored: /[\/\\]\.(git|DS_Store)/, persistent: true, ignoreInitial: true });
|
||||
this.watcher.on('add', (file: string) => {
|
||||
this.scheduleCompile();
|
||||
});
|
||||
this.watcher.on('change', (file: string) => {
|
||||
this.scheduleCompile();
|
||||
});
|
||||
this.watcher.on('unlink', (file: string) => {
|
||||
this.scheduleCompile();
|
||||
});
|
||||
this.startCompilationServer();
|
||||
this.triggerCompilationServer();
|
||||
}
|
||||
else {
|
||||
try {
|
||||
await this.compile();
|
||||
}
|
||||
catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
scheduleCompile() {
|
||||
if (this.ready) {
|
||||
this.triggerCompilationServer();
|
||||
}
|
||||
else {
|
||||
this.todo = true;
|
||||
}
|
||||
}
|
||||
|
||||
runHaxeAgain(parameters: string[], onClose: (code: number, signal: string) => void): child_process.ChildProcess {
|
||||
let exe = 'haxe';
|
||||
let env = process.env;
|
||||
if (fs.existsSync(this.haxeDirectory) && fs.statSync(this.haxeDirectory).isDirectory()) {
|
||||
let localexe = path.resolve(this.haxeDirectory, 'haxe' + sys());
|
||||
if (!fs.existsSync(localexe)) localexe = path.resolve(this.haxeDirectory, 'haxe');
|
||||
if (fs.existsSync(localexe)) exe = localexe;
|
||||
const stddir = path.resolve(this.haxeDirectory, 'std');
|
||||
if (fs.existsSync(stddir) && fs.statSync(stddir).isDirectory()) {
|
||||
env.HAXE_STD_PATH = stddir;
|
||||
}
|
||||
}
|
||||
|
||||
let haxe = child_process.spawn(exe, parameters, {env: env, cwd: path.normalize(this.from)});
|
||||
|
||||
haxe.stdout.on('data', (data: any) => {
|
||||
log.info(data.toString());
|
||||
});
|
||||
|
||||
haxe.stderr.on('data', (data: any) => {
|
||||
log.error(data.toString());
|
||||
});
|
||||
|
||||
haxe.on('close', onClose);
|
||||
|
||||
return haxe;
|
||||
}
|
||||
|
||||
runHaxeAgainAndWait(parameters: string[]): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this.runHaxeAgain(parameters, (code, signal) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
}
|
||||
else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static cleanHxml(hxml: string): string {
|
||||
let params: string[] = [];
|
||||
let ignoreNext = false;
|
||||
let parameters = hxml.split('\n');
|
||||
for (let parameter of parameters) {
|
||||
if (!parameter.startsWith('-main') && !parameter.startsWith('-js')) {
|
||||
params.push(parameter);
|
||||
}
|
||||
}
|
||||
return params.join('\n');
|
||||
}
|
||||
|
||||
runHaxe(parameters: string[], onClose: (code: number, signal: string) => void): child_process.ChildProcess {
|
||||
if (fs.existsSync(path.join(this.resourceDir, 'workers.txt'))) {
|
||||
fs.unlinkSync(path.join(this.resourceDir, 'workers.txt'));
|
||||
}
|
||||
let haxe = this.runHaxeAgain(parameters, async (code: number, signal: string) => {
|
||||
if (fs.existsSync(path.join(this.resourceDir, 'workers.txt'))) {
|
||||
let hxml = fs.readFileSync(path.join(this.from, parameters[0]), {encoding: 'utf8'});
|
||||
let workers = fs.readFileSync(path.join(this.resourceDir, 'workers.txt'), {encoding: 'utf8'});
|
||||
let lines = workers.split('\n');
|
||||
for (let line of lines) {
|
||||
if (line.trim() === '') continue;
|
||||
log.info('Creating ' + line + ' worker.');
|
||||
let newhxml = HaxeCompiler.cleanHxml(hxml);
|
||||
newhxml += '-main ' + line.trim() + '\n';
|
||||
newhxml += '-js ' + path.join(this.sysdir, line.trim()) + '.js\n';
|
||||
newhxml += '-D kha_in_worker\n';
|
||||
fs.writeFileSync(path.join(this.from, 'temp.hxml'), newhxml, {encoding: 'utf8'});
|
||||
await this.runHaxeAgainAndWait(['temp.hxml']);
|
||||
}
|
||||
onClose(code, signal);
|
||||
}
|
||||
else {
|
||||
onClose(code, signal);
|
||||
}
|
||||
});
|
||||
|
||||
return haxe;
|
||||
}
|
||||
|
||||
startCompilationServer() {
|
||||
this.compilationServer = this.runHaxe(['--wait', this.port], (code: number) => {
|
||||
log.info('Haxe compilation server stopped.');
|
||||
});
|
||||
}
|
||||
|
||||
triggerCompilationServer(): Promise<void> {
|
||||
process.stdout.write('\x1Bc');
|
||||
log.info('Haxe compilation...');
|
||||
this.ready = false;
|
||||
this.todo = false;
|
||||
return new Promise((resolve, reject) => {
|
||||
this.runHaxe(['--connect', this.port, this.hxml], (code: number) => {
|
||||
if (this.to && fs.existsSync(path.join(this.from, this.temp))) {
|
||||
fs.renameSync(path.join(this.from, this.temp), path.join(this.from, this.to));
|
||||
}
|
||||
this.ready = true;
|
||||
if (code === 0) {
|
||||
process.stdout.write('\x1Bc');
|
||||
log.info('Haxe compile end.');
|
||||
if (this.isLiveReload) {
|
||||
this.wsClients.forEach(client => {
|
||||
client.send(JSON.stringify({}));
|
||||
});
|
||||
}
|
||||
for (let callback of Callbacks.postHaxeRecompilation) {
|
||||
callback();
|
||||
}
|
||||
} else {
|
||||
log.info('Haxe compile error.');
|
||||
}
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
}
|
||||
|
||||
// (node:3630) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future,
|
||||
// promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
|
||||
// else reject('Haxe compiler error.');
|
||||
|
||||
if (this.todo) {
|
||||
this.scheduleCompile();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
compile(): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this.runHaxe([this.hxml], (code: number) => {
|
||||
if (code === 0) {
|
||||
if (this.to && fs.existsSync(path.join(this.from, this.temp))) {
|
||||
fs.renameSync(path.join(this.from, this.temp), path.join(this.from, this.to));
|
||||
}
|
||||
resolve();
|
||||
}
|
||||
else {
|
||||
process.exitCode = 1;
|
||||
log.error('Haxe compiler error.');
|
||||
reject();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static spinRename(from: string, to: string): void {
|
||||
for (; ; ) {
|
||||
if (fs.existsSync(from)) {
|
||||
fs.renameSync(from, to);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
438
Kha/Tools/khamake/src/HaxeProject.ts
Normal file
438
Kha/Tools/khamake/src/HaxeProject.ts
Normal file
@ -0,0 +1,438 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import {writeXml} from './XmlWriter';
|
||||
import * as log from './log';
|
||||
|
||||
function copyAndReplace(from: string, to: string, names: string[], values: string[]) {
|
||||
let data = fs.readFileSync(from, { encoding: 'utf8' });
|
||||
for (let i = 0; i < names.length; ++i) {
|
||||
data = data.replace(new RegExp(names[i], 'g'), values[i]);
|
||||
}
|
||||
fs.writeFileSync(to, data, { encoding: 'utf8' });
|
||||
}
|
||||
|
||||
function escapeXml(s: string) {
|
||||
return s.replace(/[<>&'"]/g, c => {
|
||||
switch (c) {
|
||||
case '<': return '<';
|
||||
case '>': return '>';
|
||||
case '&': return '&';
|
||||
case '\'': return ''';
|
||||
case '"': return '"';
|
||||
default: throw 'unreachable code';
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function IntelliJ(projectdir: string, options: any) {
|
||||
let indir = path.join(__dirname, '..', 'Data', 'intellij');
|
||||
let outdir = path.join(projectdir, options.safeName + '-' + options.system + '-intellij');
|
||||
|
||||
let sources = '';
|
||||
for (let i = 0; i < options.sources.length; ++i) {
|
||||
if (path.isAbsolute(options.sources[i])) {
|
||||
sources += ' <sourceFolder url="file://' + options.sources[i] + '" isTestSource="false" />\n';
|
||||
}
|
||||
else {
|
||||
sources += ' <sourceFolder url="file://$MODULE_DIR$/' + path.relative(outdir, path.resolve(options.from, options.sources[i])).replace(/\\/g, '/') + '" isTestSource="false" />\n';
|
||||
}
|
||||
}
|
||||
let libraries = '';
|
||||
for (let i = 0; i < options.libraries.length; ++i) {
|
||||
if (path.isAbsolute(options.libraries[i].libpath)) {
|
||||
libraries += ' <content url="file://' + options.libraries[i].libroot + '">\n';
|
||||
libraries += ' <sourceFolder url="file://' + options.libraries[i].libpath + '" isTestSource="false" />\n';
|
||||
}
|
||||
else {
|
||||
libraries += ' <content url="file://$MODULE_DIR$/' + path.relative(outdir, path.resolve(options.from, options.libraries[i].libroot)).replace(/\\/g, '/') + '">\n';
|
||||
libraries += ' <sourceFolder url="file://$MODULE_DIR$/' + path.relative(outdir, path.resolve(options.from, options.libraries[i].libpath)).replace(/\\/g, '/') + '" isTestSource="false" />\n';
|
||||
}
|
||||
libraries += ' </content>\n';
|
||||
}
|
||||
|
||||
let args = '';
|
||||
|
||||
let defines = '';
|
||||
for (let i = 0; i < options.defines.length; ++i) {
|
||||
defines += options.defines[i];
|
||||
if (i < options.defines.length - 1) defines += ',';
|
||||
}
|
||||
for (let param of options.parameters) {
|
||||
defines += param + ',';
|
||||
}
|
||||
|
||||
let target: string;
|
||||
switch (options.language) {
|
||||
case 'hl':
|
||||
case 'cpp':
|
||||
target = 'C++';
|
||||
break;
|
||||
case 'as':
|
||||
target = 'Flash';
|
||||
args = '-swf-version 16.0';
|
||||
break;
|
||||
case 'cs':
|
||||
target = 'C#';
|
||||
if (fs.existsSync(options.haxeDirectory) && fs.statSync(options.haxeDirectory).isDirectory() && fs.existsSync(path.join(options.haxeDirectory, 'netlib'))) {
|
||||
args = '-net-std ' + path.relative(outdir, path.join(options.haxeDirectory, 'netlib'));
|
||||
}
|
||||
break;
|
||||
case 'java':
|
||||
target = 'Java';
|
||||
if (fs.existsSync(options.haxeDirectory) && fs.statSync(options.haxeDirectory).isDirectory() && fs.existsSync(path.join(options.haxeDirectory, 'hxjava', 'hxjava-std.jar'))) {
|
||||
args = '-java-lib ' + path.relative(outdir, path.join(options.haxeDirectory, 'hxjava', 'hxjava-std.jar'));
|
||||
}
|
||||
break;
|
||||
case 'js':
|
||||
target = 'JavaScript';
|
||||
break;
|
||||
}
|
||||
|
||||
fs.copySync(path.join(indir, 'name.iml'), path.join(outdir, options.name + '.iml'), { overwrite: true });
|
||||
copyAndReplace(path.join(indir, 'name.iml'), path.join(outdir, options.name + '.iml'), ['{name}', '{sources}', '{libraries}', '{target}', '{system}', '{args}'], [options.safeName, sources, libraries, target, options.system, args]);
|
||||
|
||||
fs.copySync(path.join(indir, 'idea', 'compiler.xml'), path.join(outdir, '.idea', 'compiler.xml'), { overwrite: true });
|
||||
copyAndReplace(path.join(indir, 'idea', 'haxe.xml'), path.join(outdir, '.idea', 'haxe.xml'), ['{defines}'], [defines]);
|
||||
fs.copySync(path.join(indir, 'idea', 'misc.xml'), path.join(outdir, '.idea', 'misc.xml'), { overwrite: true });
|
||||
copyAndReplace(path.join(indir, 'idea', 'modules.xml'), path.join(outdir, '.idea', 'modules.xml'), ['{name}'], [options.name]);
|
||||
fs.copySync(path.join(indir, 'idea', 'vcs.xml'), path.join(outdir, '.idea', 'vcs.xml'), { overwrite: true });
|
||||
copyAndReplace(path.join(indir, 'idea', 'name'), path.join(outdir, '.idea', '.name'), ['{name}'], [options.name]);
|
||||
fs.copySync(path.join(indir, 'idea', 'copyright', 'profiles_settings.xml'), path.join(outdir, '.idea', 'copyright', 'profiles_settings.xml'), { overwrite: true });
|
||||
}
|
||||
|
||||
function hxml(projectdir: string, options: any) {
|
||||
let data = '';
|
||||
let lines: Array<String> = [];
|
||||
|
||||
// returns only unique lines and '' otherwise
|
||||
function unique(line: String): String {
|
||||
if (lines.indexOf(line) === -1) {
|
||||
lines.push(line);
|
||||
return line;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
for (let i = 0; i < options.sources.length; ++i) {
|
||||
if (path.isAbsolute(options.sources[i])) {
|
||||
data += unique('-cp ' + options.sources[i] + '\n');
|
||||
}
|
||||
else {
|
||||
data += unique('-cp ' + path.relative(projectdir, path.resolve(options.from, options.sources[i])) + '\n'); // from.resolve('build').relativize(from.resolve(this.sources[i])).toString());
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < options.libraries.length; ++i) {
|
||||
if (path.isAbsolute(options.libraries[i].libpath)) {
|
||||
data += unique('-cp ' + options.libraries[i].libpath + '\n');
|
||||
}
|
||||
else {
|
||||
data += unique('-cp ' + path.relative(projectdir, path.resolve(options.from, options.libraries[i].libpath)) + '\n'); // from.resolve('build').relativize(from.resolve(this.sources[i])).toString());
|
||||
}
|
||||
}
|
||||
for (let d in options.defines) {
|
||||
let define = options.defines[d];
|
||||
data += unique('-D ' + define + '\n');
|
||||
}
|
||||
if (options.language === 'cpp') {
|
||||
data += unique('-cpp ' + path.normalize(options.to) + '\n');
|
||||
}
|
||||
else if (options.language === 'cs') {
|
||||
data += unique('-cs ' + path.normalize(options.to) + '\n');
|
||||
if (fs.existsSync(options.haxeDirectory) && fs.statSync(options.haxeDirectory).isDirectory() && fs.existsSync(path.join(options.haxeDirectory, 'netlib'))) {
|
||||
data += unique('-net-std ' + path.relative(projectdir, path.join(options.haxeDirectory, 'netlib')) + '\n');
|
||||
}
|
||||
}
|
||||
else if (options.language === 'java') {
|
||||
data += unique('-java ' + path.normalize(options.to) + '\n');
|
||||
if (fs.existsSync(options.haxeDirectory) && fs.statSync(options.haxeDirectory).isDirectory() && fs.existsSync(path.join(options.haxeDirectory, 'hxjava', 'hxjava-std.jar'))) {
|
||||
data += unique('-java-lib ' + path.relative(projectdir, path.join(options.haxeDirectory, 'hxjava', 'hxjava-std.jar')) + '\n');
|
||||
}
|
||||
}
|
||||
else if (options.language === 'js') {
|
||||
data += unique('-js ' + path.normalize(options.to) + '\n');
|
||||
}
|
||||
else if (options.language === 'as') {
|
||||
data += unique('-swf ' + path.normalize(options.to) + '\n');
|
||||
data += unique('-swf-version ' + options.swfVersion + '\n');
|
||||
data += unique('-swf-header ' + options.width + ':' + options.height + ':' + options.framerate + ':' + options.stageBackground + '\n');
|
||||
}
|
||||
else if (options.language === 'xml') {
|
||||
data += unique('-xml ' + path.normalize(options.to) + '\n');
|
||||
data += unique('--macro include(\'kha\')\n');
|
||||
}
|
||||
else if (options.language === 'hl') {
|
||||
data += unique('-hl ' + path.normalize(options.to) + '\n');
|
||||
}
|
||||
for (let param of options.parameters) {
|
||||
data += unique(param + '\n');
|
||||
}
|
||||
|
||||
if (!options.parameters.some((param: string) => param.includes('-main '))) {
|
||||
const entrypoint = options ? options.main ? options.main : 'Main' : 'Main';
|
||||
data += unique('-main ' + entrypoint + '\n');
|
||||
}
|
||||
|
||||
fs.outputFileSync(path.join(projectdir, 'project-' + options.system + '.hxml'), data);
|
||||
}
|
||||
|
||||
function FlashDevelop(projectdir: string, options: any) {
|
||||
let platform: string;
|
||||
|
||||
switch (options.language) {
|
||||
case 'hl':
|
||||
case 'cpp':
|
||||
platform = 'C++';
|
||||
break;
|
||||
case 'as':
|
||||
platform = 'Flash Player';
|
||||
break;
|
||||
case 'cs':
|
||||
platform = 'C#';
|
||||
break;
|
||||
case 'java':
|
||||
platform = 'Java';
|
||||
break;
|
||||
case 'js':
|
||||
platform = 'JavaScript';
|
||||
break;
|
||||
}
|
||||
|
||||
options.swfVersion = 'swfVersion' in options ? options.swfVersion : 16.0;
|
||||
options.stageBackground = 'stageBackground' in options ? options.stageBackground : 'ffffff';
|
||||
options.framerate = 'framerate' in options ? options.framerate : 30;
|
||||
|
||||
let swfVersion = parseFloat(options.swfVersion).toFixed(1).split('.');
|
||||
|
||||
let output: any = {
|
||||
n: 'output',
|
||||
e: [
|
||||
{
|
||||
n: 'movie',
|
||||
outputType: 'Application'
|
||||
},
|
||||
{
|
||||
n: 'movie',
|
||||
input: ''
|
||||
},
|
||||
{
|
||||
n: 'movie',
|
||||
path: path.normalize(options.to)
|
||||
},
|
||||
{
|
||||
n: 'movie',
|
||||
fps: options.framerate
|
||||
},
|
||||
{
|
||||
n: 'movie',
|
||||
width: options.width
|
||||
},
|
||||
{
|
||||
n: 'movie',
|
||||
height: options.height
|
||||
},
|
||||
{
|
||||
n: 'movie',
|
||||
version: swfVersion[0]
|
||||
},
|
||||
{
|
||||
n: 'movie',
|
||||
minorVersion: swfVersion[1]
|
||||
},
|
||||
{
|
||||
n: 'movie',
|
||||
platform: platform
|
||||
},
|
||||
{
|
||||
n: 'movie',
|
||||
background: '#' + options.stageBackground
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
if (fs.existsSync(options.haxeDirectory) && fs.statSync(options.haxeDirectory).isDirectory()) {
|
||||
output.e.push({
|
||||
n: 'movie',
|
||||
preferredSDK: path.relative(projectdir, options.haxeDirectory)
|
||||
});
|
||||
}
|
||||
|
||||
let classpaths: string[] = [];
|
||||
|
||||
for (let i = 0; i < options.sources.length; ++i) {
|
||||
classpaths.push(path.relative(projectdir, path.resolve(options.from, options.sources[i])));
|
||||
}
|
||||
|
||||
for (let i = 0; i < options.libraries.length; ++i) {
|
||||
classpaths.push(path.relative(projectdir, path.resolve(options.from, options.libraries[i].libpath)));
|
||||
}
|
||||
|
||||
let otheroptions: any = [
|
||||
{
|
||||
n: 'option',
|
||||
showHiddenPaths: 'False'
|
||||
}
|
||||
];
|
||||
|
||||
if (options.language === 'cpp' || options.system === 'krom') {
|
||||
otheroptions.push({
|
||||
n: 'option',
|
||||
testMovie: 'Custom'
|
||||
});
|
||||
otheroptions.push({
|
||||
n: 'option',
|
||||
testMovieCommand: 'run_' + options.system + '.bat'
|
||||
});
|
||||
}
|
||||
else if (options.language === 'cs' || options.language === 'java') {
|
||||
otheroptions.push({
|
||||
n: 'option',
|
||||
testMovie: 'OpenDocument'
|
||||
});
|
||||
otheroptions.push({
|
||||
n: 'option',
|
||||
testMovieCommand: ''
|
||||
});
|
||||
}
|
||||
else if (options.language === 'js') {
|
||||
otheroptions.push({
|
||||
n: 'option',
|
||||
testMovie: 'Webserver'
|
||||
});
|
||||
otheroptions.push({
|
||||
n: 'option',
|
||||
testMovieCommand: path.join(path.parse(options.to).dir, 'index.html')
|
||||
});
|
||||
}
|
||||
else {
|
||||
otheroptions.push({
|
||||
n: 'option',
|
||||
testMovie: 'Default'
|
||||
});
|
||||
}
|
||||
|
||||
let def = '';
|
||||
for (let d of options.defines) {
|
||||
def += '-D ' + d + '
';
|
||||
}
|
||||
if (options.language === 'java' && fs.existsSync(options.haxeDirectory) && fs.statSync(options.haxeDirectory).isDirectory() && fs.existsSync(path.join(options.haxeDirectory, 'hxjava', 'hxjava-std.jar'))) {
|
||||
def += '-java-lib ' + path.relative(projectdir, path.join(options.haxeDirectory, 'hxjava', 'hxjava-std.jar')) + '
';
|
||||
}
|
||||
if (options.language === 'cs' && fs.existsSync(options.haxeDirectory) && fs.statSync(options.haxeDirectory).isDirectory() && fs.existsSync(path.join(options.haxeDirectory, 'netlib'))) {
|
||||
def += '-net-std ' + path.relative(projectdir, path.join(options.haxeDirectory, 'netlib')) + '
';
|
||||
}
|
||||
def += '-D kha_output="' + path.resolve(path.join(projectdir, options.to)) + '"
';
|
||||
let mainClass = 'Main';
|
||||
for (let param of options.parameters) {
|
||||
const mainRe = /-main\s+([^\s]+)/.exec(param);
|
||||
if (mainRe) {
|
||||
mainClass = mainRe[1];
|
||||
}
|
||||
else {
|
||||
def += escapeXml(param) + '
';
|
||||
}
|
||||
}
|
||||
|
||||
let project = {
|
||||
n: 'project',
|
||||
version: '2',
|
||||
e: [
|
||||
'Output SWF options',
|
||||
output,
|
||||
'Other classes to be compiled into your SWF',
|
||||
{
|
||||
n: 'classpaths',
|
||||
e: classpaths
|
||||
.reduce((a, b) => {
|
||||
if (a.indexOf(b) < 0) a.push(b);
|
||||
return a;
|
||||
}, [] )
|
||||
.map((e) => {
|
||||
return {n: 'class', path: e};
|
||||
})
|
||||
},
|
||||
'Build options',
|
||||
{
|
||||
n: 'build',
|
||||
e: [
|
||||
{
|
||||
n: 'option',
|
||||
directives: ''
|
||||
},
|
||||
{
|
||||
n: 'option',
|
||||
flashStrict: 'False'
|
||||
},
|
||||
{
|
||||
n: 'option',
|
||||
noInlineOnDebug: 'False'
|
||||
},
|
||||
{
|
||||
n: 'option',
|
||||
mainClass: mainClass
|
||||
},
|
||||
{
|
||||
n: 'option',
|
||||
enabledebug: options.language === 'as' ? 'True' : 'False'
|
||||
},
|
||||
{
|
||||
n: 'option',
|
||||
additional: def
|
||||
}
|
||||
]
|
||||
},
|
||||
'haxelib libraries',
|
||||
{
|
||||
n: 'haxelib',
|
||||
e: [
|
||||
'example: <library name="..." />'
|
||||
]
|
||||
},
|
||||
'Class files to compile (other referenced classes will automatically be included)',
|
||||
{
|
||||
n: 'compileTargets',
|
||||
e: [
|
||||
{
|
||||
n: 'compile',
|
||||
path: '..\\Sources\\Main.hx'
|
||||
}
|
||||
]
|
||||
},
|
||||
'Paths to exclude from the Project Explorer tree',
|
||||
{
|
||||
n: 'hiddenPaths',
|
||||
e: [
|
||||
'example: <hidden path="..." />'
|
||||
]
|
||||
},
|
||||
'Executed before build',
|
||||
{
|
||||
n: 'preBuildCommand'
|
||||
},
|
||||
'Executed after build',
|
||||
{
|
||||
n: 'postBuildCommand',
|
||||
alwaysRun: 'False'
|
||||
},
|
||||
'Other project options',
|
||||
{
|
||||
n: 'options',
|
||||
e: otheroptions
|
||||
},
|
||||
'Plugin storage',
|
||||
{
|
||||
n: 'storage'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
writeXml(project, path.join(projectdir, options.safeName + '-' + options.system + '.hxproj'));
|
||||
}
|
||||
|
||||
export function writeHaxeProject(projectdir: string, projectFiles: boolean, options: any) {
|
||||
hxml(projectdir, options);
|
||||
if (projectFiles) {
|
||||
FlashDevelop(projectdir, options);
|
||||
IntelliJ(projectdir, options);
|
||||
}
|
||||
}
|
58
Kha/Tools/khamake/src/Icon.ts
Normal file
58
Kha/Tools/khamake/src/Icon.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import * as cp from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as log from './log';
|
||||
import * as exec from './exec';
|
||||
|
||||
function run(exe: string, from: string, to: string, width: number, height: number, format: string, background: number, transparent: boolean, callback: any) {
|
||||
let params = ['from=' + from, 'to=' + to, 'format=' + format, 'keepaspect'];
|
||||
if (width > 0) params.push('width=' + width);
|
||||
if (height > 0) params.push('height=' + height);
|
||||
if (background !== undefined && !transparent) params.push('background=' + background.toString(16));
|
||||
if (transparent) params.push('transparent=' + background.toString(16));
|
||||
let child = cp.spawn(exe, params);
|
||||
|
||||
child.stdout.on('data', (data: any) => {
|
||||
// log.info('kraffiti stdout: ' + data);
|
||||
});
|
||||
|
||||
child.stderr.on('data', (data: any) => {
|
||||
log.error('kraffiti stderr: ' + data);
|
||||
});
|
||||
|
||||
child.on('error', (err: any) => {
|
||||
log.error('kraffiti error: ' + err);
|
||||
});
|
||||
|
||||
child.on('close', (code: number) => {
|
||||
if (code !== 0) log.error('kraffiti exited with code ' + code);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function findIcon(icon: string, from: string, options: any) {
|
||||
if (icon && fs.existsSync(path.join(from, icon))) return path.join(from, icon);
|
||||
if (fs.existsSync(path.join(from, 'icon.png'))) return path.join(from, 'icon.png');
|
||||
else return path.join(options.kha, 'Kinc', 'Tools', exec.sysdir(), 'icon.png');
|
||||
}
|
||||
|
||||
export function exportIco(icon: string, to: string, from: string, options: any) {
|
||||
run(options.kraffiti, findIcon(icon, from.toString(), options), to.toString(), 0, 0, 'ico', undefined, false, function () { });
|
||||
}
|
||||
|
||||
export function exportIcns(icon: string, to: string, from: string, options: any) {
|
||||
run(options.kraffiti, findIcon(icon, from.toString(), options), to.toString(), 0, 0, 'icns', undefined, false, function () { });
|
||||
}
|
||||
|
||||
export function exportPng(icon: string, to: string, width: number, height: number, background: number, transparent: boolean, from: string, options: any) {
|
||||
run(options.kraffiti, findIcon(icon, from.toString(), options), to.toString(), width, height, 'png', background, transparent, function () { });
|
||||
}
|
||||
|
||||
export function exportPng24(icon: string, to: string, width: number, height: number, background: number, transparent: boolean, from: string, options: any) {
|
||||
run(options.kraffiti, findIcon(icon, from.toString(), options), to.toString(), width, height, 'png24', background, transparent, function () { });
|
||||
}
|
||||
|
||||
export function exportBmp(icon: string, to: string, width: number, height: number, background: number, from: string, options: any) {
|
||||
run(options.kraffiti, findIcon(icon, from.toString(), options), to.toString(), width, height, 'bmp', background, false, function () { });
|
||||
}
|
171
Kha/Tools/khamake/src/ImageTool.ts
Normal file
171
Kha/Tools/khamake/src/ImageTool.ts
Normal file
@ -0,0 +1,171 @@
|
||||
import * as child_process from 'child_process';
|
||||
import * as fs from 'fs-extra';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as log from './log';
|
||||
import {sys} from './exec';
|
||||
|
||||
function getWidthAndHeight(kha: string, exe: string, from: string, to: string, options: any, format: string, prealpha: boolean): Promise<{w: number, h: number}> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let params = ['from=' + from, 'to=' + to, 'format=' + format, 'donothing'];
|
||||
if (options.scale !== undefined && options.scale !== 1) {
|
||||
params.push('scale=' + options.scale);
|
||||
}
|
||||
let process = child_process.spawn(exe, params);
|
||||
|
||||
let output = '';
|
||||
process.stdout.on('data', (data: any) => {
|
||||
output += data.toString();
|
||||
});
|
||||
|
||||
process.stderr.on('data', (data: any) => {
|
||||
|
||||
});
|
||||
|
||||
process.on('close', (code: number) => {
|
||||
if (code !== 0) {
|
||||
log.error('kraffiti process exited with code ' + code + ' when trying to get size of ' + path.parse(from).name);
|
||||
resolve({w: 0, h: 0});
|
||||
return;
|
||||
}
|
||||
const lines = output.split('\n');
|
||||
for (let line of lines) {
|
||||
if (line.startsWith('#')) {
|
||||
let numbers = line.substring(1).split('x');
|
||||
resolve({w: parseInt(numbers[0]), h: parseInt(numbers[1])});
|
||||
return;
|
||||
}
|
||||
}
|
||||
resolve({w: 0, h: 0});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function convertImage(from: string, temp: string, to: string, kha: string, exe: string, params: string[], options: any, cache: any): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
let process = child_process.spawn(exe, params);
|
||||
|
||||
let output = '';
|
||||
process.stdout.on('data', (data: any) => {
|
||||
output += data.toString();
|
||||
});
|
||||
|
||||
process.stderr.on('data', (data: any) => {
|
||||
|
||||
});
|
||||
|
||||
process.on('close', (code: number) => {
|
||||
if (code !== 0) {
|
||||
log.error('kraffiti process exited with code ' + code + ' when trying to convert ' + path.parse(from).name);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
fs.renameSync(temp, to);
|
||||
|
||||
const lines = output.split('\n');
|
||||
for (let line of lines) {
|
||||
if (line.startsWith('#')) {
|
||||
let numbers = line.substring(1).split('x');
|
||||
cache[to] = {};
|
||||
cache[to].original_width = options.original_width = parseInt(numbers[0]);
|
||||
cache[to].original_height = options.original_height = parseInt(numbers[1]);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function exportImage(kha: string, exe: string, from: string, to: string, options: any, format: string, prealpha: boolean, poweroftwo: boolean, cache: any): Promise<string> {
|
||||
if (format === undefined) {
|
||||
if (from.toString().endsWith('.png')) format = 'png';
|
||||
else if (from.toString().endsWith('.hdr')) format = 'hdr';
|
||||
else format = 'jpg';
|
||||
}
|
||||
|
||||
if (format === 'jpg' && (options.scale === undefined || options.scale === 1) && options.background === undefined) {
|
||||
to = to + '.jpg';
|
||||
}
|
||||
else if (format === 'pvr') {
|
||||
to = to + '.pvr';
|
||||
}
|
||||
else if (format === 'ASTC') {
|
||||
to = to + '.astc.k';
|
||||
}
|
||||
else if (format === 'DXT5') {
|
||||
to = to + '.dxt5.k';
|
||||
}
|
||||
else if (format === 'hdr') {
|
||||
to = to + '.hdr';
|
||||
}
|
||||
else if (format === 'lz4') {
|
||||
to += '.k';
|
||||
}
|
||||
else {
|
||||
format = 'png';
|
||||
if (prealpha) to = to + '.kng';
|
||||
else to = to + '.png';
|
||||
}
|
||||
|
||||
let temp = to + '.temp';
|
||||
|
||||
let outputformat = format;
|
||||
if (format === 'png' && prealpha) {
|
||||
outputformat = 'kng';
|
||||
}
|
||||
if (format === 'lz4') {
|
||||
outputformat = 'k';
|
||||
}
|
||||
if (format === 'ASTC') {
|
||||
outputformat = 'astc.k';
|
||||
}
|
||||
if (format === 'DXT5') {
|
||||
outputformat = 'dxt5.k';
|
||||
}
|
||||
|
||||
if (fs.existsSync(to) && fs.statSync(to).mtime.getTime() > fs.statSync(from.toString()).mtime.getTime()) {
|
||||
if (cache[to] !== undefined) {
|
||||
const cachedOptions = cache[to];
|
||||
options.original_width = cachedOptions.original_width;
|
||||
options.original_height = cachedOptions.original_height;
|
||||
return outputformat;
|
||||
}
|
||||
let wh = await getWidthAndHeight(kha, exe, from, to, options, format, prealpha);
|
||||
cache[to] = {};
|
||||
cache[to].original_width = options.original_width = wh.w;
|
||||
cache[to].original_height = options.original_height = wh.h;
|
||||
return outputformat;
|
||||
}
|
||||
|
||||
fs.ensureDirSync(path.dirname(to));
|
||||
|
||||
if (format === 'jpg' || format === 'hdr') {
|
||||
fs.copySync(from, temp, { overwrite: true });
|
||||
fs.renameSync(temp, to);
|
||||
let wh = await getWidthAndHeight(kha, exe, from, to, options, format, prealpha);
|
||||
options.original_width = wh.w;
|
||||
options.original_height = wh.h;
|
||||
return outputformat;
|
||||
}
|
||||
|
||||
let params = ['from=' + from, 'to=' + temp, 'format=' + format];
|
||||
if (!poweroftwo) {
|
||||
params.push('filter=nearest');
|
||||
}
|
||||
if (prealpha) params.push('prealpha');
|
||||
if (options.scale !== undefined && options.scale !== 1) {
|
||||
params.push('scale=' + options.scale);
|
||||
}
|
||||
if (options.background !== undefined) {
|
||||
params.push('transparent=' + ((options.background.red << 24) | (options.background.green << 16) | (options.background.blue << 8) | 0xff).toString(16));
|
||||
}
|
||||
if (poweroftwo) {
|
||||
params.push('poweroftwo');
|
||||
}
|
||||
|
||||
await convertImage(from, temp, to, kha, exe, params, options, cache);
|
||||
return outputformat;
|
||||
}
|
51
Kha/Tools/khamake/src/Options.ts
Normal file
51
Kha/Tools/khamake/src/Options.ts
Normal file
@ -0,0 +1,51 @@
|
||||
export class Options {
|
||||
from: string;
|
||||
to: string;
|
||||
projectfile: string;
|
||||
target: string;
|
||||
vr: string;
|
||||
raytrace: string;
|
||||
main: string;
|
||||
// intermediate: string;
|
||||
graphics: string;
|
||||
arch: string;
|
||||
audio: string;
|
||||
visualstudio: string;
|
||||
kha: string;
|
||||
haxe: string;
|
||||
nohaxe: boolean;
|
||||
ffmpeg: string;
|
||||
krafix: string;
|
||||
kraffiti: string;
|
||||
noshaders: boolean;
|
||||
|
||||
parallelAssetConversion: number;
|
||||
noproject: boolean;
|
||||
onlydata: boolean;
|
||||
embedflashassets: boolean;
|
||||
compile: boolean;
|
||||
run: boolean;
|
||||
init: boolean;
|
||||
name: string;
|
||||
server: boolean;
|
||||
port: string;
|
||||
debug: boolean;
|
||||
silent: boolean;
|
||||
quiet: boolean;
|
||||
watch: boolean;
|
||||
watchport: string;
|
||||
livereload: boolean;
|
||||
glsl2: boolean;
|
||||
shaderversion: string;
|
||||
|
||||
ogg: string;
|
||||
aac: string;
|
||||
mp3: string;
|
||||
h264: string;
|
||||
webm: string;
|
||||
wmv: string;
|
||||
theora: string;
|
||||
|
||||
slowgc: boolean;
|
||||
nosigning: boolean;
|
||||
}
|
24
Kha/Tools/khamake/src/Platform.ts
Normal file
24
Kha/Tools/khamake/src/Platform.ts
Normal file
@ -0,0 +1,24 @@
|
||||
export const Platform = {
|
||||
Krom: 'krom',
|
||||
Windows: 'windows',
|
||||
WindowsApp: 'windowsapp',
|
||||
PlayStation3: 'ps3',
|
||||
iOS: 'ios',
|
||||
OSX: 'osx',
|
||||
Android: 'android',
|
||||
Xbox360: 'xbox360',
|
||||
Linux: 'linux',
|
||||
HTML5: 'html5',
|
||||
HTML5Worker: 'html5worker',
|
||||
Flash: 'flash',
|
||||
WPF: 'wpf',
|
||||
Java: 'java',
|
||||
PlayStationMobile: 'psm',
|
||||
Node: 'node',
|
||||
DebugHTML5: 'debug-html5',
|
||||
Empty: 'empty',
|
||||
Pi: 'pi',
|
||||
tvOS: 'tvos',
|
||||
FreeBSD: 'freebsd',
|
||||
Emscripten: 'emscripten'
|
||||
};
|
336
Kha/Tools/khamake/src/Project.ts
Normal file
336
Kha/Tools/khamake/src/Project.ts
Normal file
@ -0,0 +1,336 @@
|
||||
import * as child_process from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as log from './log';
|
||||
import {loadProject} from './ProjectFile';
|
||||
|
||||
export class Library {
|
||||
libpath: string;
|
||||
libroot: string;
|
||||
}
|
||||
|
||||
export class Target {
|
||||
baseTarget: string;
|
||||
backends: string[];
|
||||
|
||||
constructor(baseTarget: string, backends: string[]) {
|
||||
this.baseTarget = baseTarget;
|
||||
this.backends = backends;
|
||||
}
|
||||
}
|
||||
|
||||
function contains(main: string, sub: string) {
|
||||
main = path.resolve(main);
|
||||
sub = path.resolve(sub);
|
||||
if (process.platform === 'win32') {
|
||||
main = main.toLowerCase();
|
||||
sub = sub.toLowerCase();
|
||||
}
|
||||
return sub.indexOf(main) === 0 && sub.slice(main.length)[0] === path.sep;
|
||||
}
|
||||
|
||||
export class Project {
|
||||
static platform: string;
|
||||
static scriptdir: string;
|
||||
name: string;
|
||||
safeName: string;
|
||||
version: string;
|
||||
sources: string[];
|
||||
defines: string[];
|
||||
cdefines: string[];
|
||||
cflags: string[];
|
||||
cppflags: string[];
|
||||
parameters: string[];
|
||||
scriptdir: string;
|
||||
libraries: Library[];
|
||||
localLibraryPath: string;
|
||||
windowOptions: any;
|
||||
targetOptions: any;
|
||||
assetMatchers: { match: string, options: any }[];
|
||||
shaderMatchers: { match: string, options: any }[];
|
||||
customTargets: Map<string, Target>;
|
||||
stackSize: number;
|
||||
id: string;
|
||||
icon: string = null;
|
||||
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
this.safeName = name.replace(/[^A-z0-9\-\_]/g, '-');
|
||||
this.version = '1.0';
|
||||
this.sources = [];
|
||||
this.defines = ['hxcpp_smart_strings'];
|
||||
this.cdefines = [];
|
||||
this.cflags = [];
|
||||
this.cppflags = [];
|
||||
this.parameters = [];
|
||||
this.scriptdir = Project.scriptdir;
|
||||
this.libraries = [];
|
||||
this.localLibraryPath = 'Libraries';
|
||||
this.assetMatchers = [];
|
||||
this.shaderMatchers = [];
|
||||
this.customTargets = new Map();
|
||||
this.stackSize = 0;
|
||||
|
||||
this.windowOptions = {};
|
||||
this.targetOptions = {
|
||||
html5: {},
|
||||
flash: {},
|
||||
android: {},
|
||||
android_native: {},
|
||||
ios: {},
|
||||
xboxOne: {},
|
||||
playStation4: {},
|
||||
switch: {},
|
||||
xboxSeriesXS: {},
|
||||
playStation5: {},
|
||||
stadia: {}
|
||||
};
|
||||
}
|
||||
|
||||
getSafeName() {
|
||||
return this.safeName;
|
||||
}
|
||||
|
||||
async addProject(projectDir: string) {
|
||||
if (!path.isAbsolute(projectDir)) {
|
||||
projectDir = path.join(this.scriptdir, projectDir);
|
||||
}
|
||||
if (!fs.existsSync(path.join(projectDir, 'khafile.js')) && (fs.existsSync(path.join(projectDir, 'kincfile.js')) || fs.existsSync(path.join(projectDir, 'korefile.js')) || fs.existsSync(path.join(projectDir, 'kfile.js')))) {
|
||||
this.libraries.push({
|
||||
libpath: projectDir,
|
||||
libroot: projectDir
|
||||
});
|
||||
}
|
||||
else {
|
||||
let project = await loadProject(projectDir, 'khafile.js', Project.platform);
|
||||
this.assetMatchers = this.assetMatchers.concat(project.assetMatchers);
|
||||
this.sources = this.sources.concat(project.sources);
|
||||
this.shaderMatchers = this.shaderMatchers.concat(project.shaderMatchers);
|
||||
this.defines = this.defines.concat(project.defines);
|
||||
this.cdefines = this.cdefines.concat(project.cdefines);
|
||||
this.cflags = this.cflags.concat(project.cflags);
|
||||
this.cppflags = this.cppflags.concat(project.cppflags);
|
||||
this.parameters = this.parameters.concat(project.parameters);
|
||||
this.libraries = this.libraries.concat(project.libraries);
|
||||
if (this.icon === null && project.icon !== null) this.icon = path.join(projectDir, project.icon);
|
||||
for (let customTarget of project.customTargets.keys()) {
|
||||
this.customTargets.set(customTarget, project.customTargets.get(customTarget));
|
||||
}
|
||||
// windowOptions and targetOptions are ignored
|
||||
}
|
||||
}
|
||||
|
||||
private unglob(str: string): string {
|
||||
const globChars = ['\\@', '\\!', '\\+', '\\*', '\\?', '\\(', '\\[', '\\{', '\\)', '\\]', '\\}'];
|
||||
str = str.replace(/\\/g, '/');
|
||||
for (const char of globChars) {
|
||||
str = str.replace(new RegExp(char, 'g'), char);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
private getBaseDir(str: string): string {
|
||||
// replace \\ to / if next char is not glob
|
||||
str = str.replace(/\\([^@!+*?{}()[\]]|$)/g, '/$1');
|
||||
// find non-globby path part
|
||||
const globby = /[^\\][@!+*?{}()[\]]/;
|
||||
while (globby.test(str)) {
|
||||
str = path.posix.dirname(str);
|
||||
}
|
||||
str = this.removeGlobEscaping(str);
|
||||
if (!str.endsWith('/')) str += '/';
|
||||
return str;
|
||||
}
|
||||
|
||||
private removeGlobEscaping(str: string): string {
|
||||
return str.replace(/\\([@!+*?{}()[\]]|$)/g, '$1');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all assets matching the match glob relative to the directory containing the current khafile.
|
||||
* Asset types are infered from the file suffix.
|
||||
* Glob syntax is very simple, the most important patterns are * for anything and ** for anything across directories.
|
||||
*/
|
||||
addAssets(match: string, options: any) {
|
||||
if (!options) options = {};
|
||||
|
||||
if (!path.isAbsolute(match)) {
|
||||
let base = this.unglob(path.resolve(this.scriptdir));
|
||||
if (!base.endsWith('/')) base += '/';
|
||||
// if there is no nameBaseDir: extract relative assets path from match
|
||||
const baseName = options.nameBaseDir == null ? this.getBaseDir(match) : options.nameBaseDir;
|
||||
match = path.posix.join(base, match.replace(/\\/g, '/'));
|
||||
options.baseDir = path.posix.join(this.removeGlobEscaping(base), baseName);
|
||||
}
|
||||
else {
|
||||
options.baseDir = this.getBaseDir(match);
|
||||
}
|
||||
|
||||
this.assetMatchers.push({ match: match, options: options });
|
||||
}
|
||||
|
||||
addSources(source: string) {
|
||||
this.sources.push(path.resolve(path.join(this.scriptdir, source)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all shaders matching the match glob relative to the directory containing the current khafile.
|
||||
* Glob syntax is very simple, the most important patterns are * for anything and ** for anything across directories.
|
||||
*/
|
||||
addShaders(match: string, options: any) {
|
||||
if (!options) options = {};
|
||||
|
||||
if (!path.isAbsolute(match)) {
|
||||
let base = this.unglob(path.resolve(this.scriptdir));
|
||||
if (!base.endsWith('/')) {
|
||||
base += '/';
|
||||
}
|
||||
match = base + match.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
this.shaderMatchers.push({ match: match, options: options });
|
||||
}
|
||||
|
||||
addDefine(define: string) {
|
||||
this.defines.push(define);
|
||||
}
|
||||
|
||||
addCDefine(define: string) {
|
||||
this.cdefines.push(define);
|
||||
}
|
||||
|
||||
addCFlag(flag: string) {
|
||||
this.cflags.push(flag);
|
||||
}
|
||||
|
||||
addCppFlag(flag: string) {
|
||||
this.cppflags.push(flag);
|
||||
}
|
||||
|
||||
addParameter(parameter: string) {
|
||||
this.parameters.push(parameter);
|
||||
}
|
||||
|
||||
addTarget(name: string, baseTarget: string, backends: string[]) {
|
||||
this.customTargets.set(name, new Target(baseTarget, backends));
|
||||
}
|
||||
|
||||
addLibrary(library: string): string {
|
||||
this.addDefine(library);
|
||||
let self = this;
|
||||
function findLibraryDirectory(name: string) {
|
||||
if (path.isAbsolute(name)) {
|
||||
return { libpath: name, libroot: name };
|
||||
}
|
||||
|
||||
// Tries to load the default library from inside the kha project.
|
||||
// e.g. 'Libraries/wyngine'
|
||||
let libpath = path.join(self.scriptdir, self.localLibraryPath, name);
|
||||
if (fs.existsSync(libpath) && fs.statSync(libpath).isDirectory()) {
|
||||
let dir = path.resolve(libpath);
|
||||
return { libpath: dir, libroot: dir };
|
||||
}
|
||||
// If the library couldn't be found in Libraries folder, try
|
||||
// looking in the haxelib folders.
|
||||
// e.g. addLibrary('hxcpp') => '/usr/lib/haxelib/hxcpp/3,2,193'
|
||||
try {
|
||||
libpath = path.join(child_process.execSync('haxelib config', { encoding: 'utf8' }).trim(), name.replace(/\./g, ','));
|
||||
}
|
||||
catch (error) {
|
||||
if (process.env.HAXEPATH) {
|
||||
libpath = path.join(process.env.HAXEPATH, 'lib', name.toLowerCase());
|
||||
}
|
||||
}
|
||||
if (fs.existsSync(libpath) && fs.statSync(libpath).isDirectory()) {
|
||||
if (fs.existsSync(path.join(libpath, '.dev'))) {
|
||||
libpath = fs.readFileSync(path.join(libpath, '.dev'), 'utf8');
|
||||
if (!path.isAbsolute(libpath)) {
|
||||
libpath = path.resolve(libpath);
|
||||
}
|
||||
return { libpath: libpath, libroot: libpath };
|
||||
}
|
||||
else if (fs.existsSync(path.join(libpath, '.current'))) {
|
||||
// Get the latest version of the haxelib path,
|
||||
// e.g. for 'hxcpp', latest version '3,2,193'
|
||||
let current = fs.readFileSync(path.join(libpath, '.current'), 'utf8');
|
||||
return { libpath: path.join(libpath, current.replace(/\./g, ',')), libroot: libpath };
|
||||
}
|
||||
}
|
||||
// check relative path
|
||||
if (fs.existsSync(path.resolve(name))) {
|
||||
let libpath = path.resolve(name);
|
||||
return { libpath: libpath, libroot: libpath };
|
||||
}
|
||||
// Show error if library isn't found in Libraries or haxelib folder
|
||||
log.error('Error: Library ' + name + ' not found.');
|
||||
log.error('Add it to the \'Libraries\' subdirectory of your project. You may also install it via haxelib but that\'s less cool.');
|
||||
throw 'Library ' + name + ' not found.';
|
||||
}
|
||||
|
||||
let libInfo = findLibraryDirectory(library);
|
||||
let dir = libInfo.libpath;
|
||||
|
||||
if (dir !== '') {
|
||||
for (let elem of this.libraries) {
|
||||
if (elem.libroot === libInfo.libroot)
|
||||
return '';
|
||||
}
|
||||
this.libraries.push({
|
||||
libpath: dir,
|
||||
libroot: libInfo.libroot
|
||||
});
|
||||
// If this is a haxelib library, there must be a haxelib.json
|
||||
if (fs.existsSync(path.join(dir, 'haxelib.json'))) {
|
||||
let options = JSON.parse(fs.readFileSync(path.join(dir, 'haxelib.json'), 'utf8'));
|
||||
// If there is a classPath value, add that directory to be loaded.
|
||||
// Otherwise, just load the current path.
|
||||
if (options.classPath) {
|
||||
// TODO find an example haxelib that has a classPath value
|
||||
this.sources.push(path.join(dir, options.classPath));
|
||||
}
|
||||
else {
|
||||
// e.g. '/usr/lib/haxelib/hxcpp/3,2,193'
|
||||
this.sources.push(dir);
|
||||
}
|
||||
// If this haxelib has other library dependencies, add them too
|
||||
if (options.dependencies) {
|
||||
for (let dependency in options.dependencies) {
|
||||
if (dependency.toLowerCase() !== 'kha') {
|
||||
this.addLibrary(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If there is no haxelib.json file, then just load the library
|
||||
// by the Sources folder.
|
||||
// e.g. Libraries/wyngine/Sources
|
||||
if (!fs.existsSync(path.join(dir, 'Sources'))) {
|
||||
log.info('Warning: No haxelib.json and no Sources directory found in library ' + library + '.');
|
||||
}
|
||||
this.sources.push(path.join(dir, 'Sources'));
|
||||
}
|
||||
|
||||
if (fs.existsSync(path.join(dir, 'extraParams.hxml'))) {
|
||||
let params = fs.readFileSync(path.join(dir, 'extraParams.hxml'), 'utf8');
|
||||
for (let parameter of params.split('\n')) {
|
||||
let param = parameter.trim();
|
||||
if (param !== '') {
|
||||
if (param.startsWith('-lib')) {
|
||||
// (DK)
|
||||
// - '-lib xxx' is for linking a library via haxe, it forces the use of the haxelib version
|
||||
// - this should be handled by khamake though, as it tracks the dependencies better (local folder or haxelib)
|
||||
log.info('Ignoring ' + dir + '/extraParams.hxml "' + param + '"');
|
||||
}
|
||||
else {
|
||||
this.addParameter(param);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.addShaders(dir + '/Sources/Shaders/**', {});
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
}
|
70
Kha/Tools/khamake/src/ProjectFile.ts
Normal file
70
Kha/Tools/khamake/src/ProjectFile.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as log from './log';
|
||||
import {Platform} from './Platform';
|
||||
import {Project} from './Project';
|
||||
|
||||
export let Callbacks = {
|
||||
preAssetConversion: [() => {}],
|
||||
preShaderCompilation: [() => {}],
|
||||
preHaxeCompilation: [() => {}],
|
||||
postHaxeCompilation: [() => {}],
|
||||
postHaxeRecompilation: [() => {}],
|
||||
postCppCompilation: [() => {}],
|
||||
postAssetReexporting: [(filePath: string) => {}],
|
||||
postBuild: [() => {}],
|
||||
onFailure: [(error: any) => {}]
|
||||
};
|
||||
|
||||
export async function loadProject(from: string, projectfile: string, platform: string): Promise<Project> {
|
||||
return new Promise<Project>((resolve, reject) => {
|
||||
fs.readFile(path.join(from, projectfile), 'utf8', (err, data) => {
|
||||
if (err) {
|
||||
throw new Error('Error reading ' + projectfile + ' from ' + from + '.');
|
||||
}
|
||||
|
||||
let resolved = false;
|
||||
let callbacks = {
|
||||
preAssetConversion: () => {},
|
||||
preShaderCompilation: () => {},
|
||||
preHaxeCompilation: () => {},
|
||||
postHaxeCompilation: () => {},
|
||||
postHaxeRecompilation: () => {},
|
||||
postCppCompilation: () => {},
|
||||
postAssetReexporting: (filePath: string) => {},
|
||||
postBuild: () => {},
|
||||
onFailure: (error: any) => {}
|
||||
};
|
||||
let resolver = (project: Project) => {
|
||||
resolved = true;
|
||||
Callbacks.preAssetConversion.push(callbacks.preAssetConversion);
|
||||
Callbacks.preShaderCompilation.push(callbacks.preShaderCompilation);
|
||||
Callbacks.preHaxeCompilation.push(callbacks.preHaxeCompilation);
|
||||
Callbacks.postHaxeCompilation.push(callbacks.postHaxeCompilation);
|
||||
Callbacks.postHaxeRecompilation.push(callbacks.postHaxeRecompilation);
|
||||
Callbacks.postCppCompilation.push(callbacks.postCppCompilation);
|
||||
Callbacks.postAssetReexporting.push(callbacks.postAssetReexporting);
|
||||
Callbacks.postBuild.push(callbacks.postBuild);
|
||||
Callbacks.onFailure.push(callbacks.onFailure);
|
||||
resolve(project);
|
||||
};
|
||||
|
||||
process.on('exit', (code: number) => {
|
||||
if (!resolved) {
|
||||
console.error('Error: khafile.js did not call resolve, no project created.');
|
||||
}
|
||||
});
|
||||
|
||||
Project.platform = platform;
|
||||
Project.scriptdir = from;
|
||||
try {
|
||||
let AsyncFunction = Object.getPrototypeOf(async () => {}).constructor;
|
||||
new AsyncFunction('Project', 'Platform', 'platform', 'require', '__dirname', 'process', 'resolve', 'reject', 'callbacks', data)
|
||||
(Project, Platform, platform, require, path.resolve(from), process, resolver, reject, callbacks);
|
||||
}
|
||||
catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
4
Kha/Tools/khamake/src/RayTraceApi.ts
Normal file
4
Kha/Tools/khamake/src/RayTraceApi.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export let RayTraceApi = {
|
||||
DXR: 'dxr',
|
||||
None: 'none'
|
||||
};
|
502
Kha/Tools/khamake/src/ShaderCompiler.ts
Normal file
502
Kha/Tools/khamake/src/ShaderCompiler.ts
Normal file
@ -0,0 +1,502 @@
|
||||
import * as child_process from 'child_process';
|
||||
import * as fs from 'fs-extra';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import * as chokidar from 'chokidar';
|
||||
import * as Throttle from 'promise-parallel-throttle';
|
||||
import {KhaExporter} from './Exporters/KhaExporter';
|
||||
import {GraphicsApi} from './GraphicsApi';
|
||||
import {Options} from './Options';
|
||||
import {Platform} from './Platform';
|
||||
import {AssetConverter} from './AssetConverter';
|
||||
import * as log from './log';
|
||||
|
||||
export interface Variable {
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export class CompiledShader {
|
||||
name: string;
|
||||
files: string[];
|
||||
inputs: Variable[];
|
||||
outputs: Variable[];
|
||||
uniforms: Variable[];
|
||||
types: any[];
|
||||
noembed: boolean;
|
||||
|
||||
constructor() {
|
||||
this.files = [];
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
this.uniforms = [];
|
||||
this.types = [];
|
||||
this.noembed = false;
|
||||
}
|
||||
}
|
||||
|
||||
export class ShaderCompiler {
|
||||
exporter: KhaExporter;
|
||||
platform: string;
|
||||
compiler: string;
|
||||
type: string;
|
||||
to: string;
|
||||
temp: string;
|
||||
builddir: string;
|
||||
options: Options;
|
||||
shaderMatchers: Array<{ match: string, options: any }>;
|
||||
watcher: fs.FSWatcher;
|
||||
|
||||
constructor(exporter: KhaExporter, platform: string, compiler: string, to: string, temp: string, builddir: string, options: Options, shaderMatchers: Array<{ match: string, options: any }>) {
|
||||
this.exporter = exporter;
|
||||
if (platform.endsWith('-native')) platform = platform.substr(0, platform.length - '-native'.length);
|
||||
if (platform.endsWith('-hl')) platform = platform.substr(0, platform.length - '-hl'.length);
|
||||
this.platform = platform;
|
||||
this.compiler = compiler;
|
||||
this.type = ShaderCompiler.findType(platform, options);
|
||||
this.options = options;
|
||||
this.to = to;
|
||||
this.temp = temp;
|
||||
this.builddir = builddir;
|
||||
this.shaderMatchers = shaderMatchers;
|
||||
}
|
||||
|
||||
close(): void {
|
||||
if (this.watcher) this.watcher.close();
|
||||
}
|
||||
|
||||
static findType(platform: string, options: Options): string {
|
||||
switch (platform) {
|
||||
case Platform.Empty:
|
||||
case Platform.Node:
|
||||
return 'glsl';
|
||||
case Platform.Flash:
|
||||
return 'agal';
|
||||
case Platform.Android:
|
||||
if (options.graphics === GraphicsApi.Vulkan || options.graphics === GraphicsApi.Default) {
|
||||
return 'spirv';
|
||||
}
|
||||
else if (options.graphics === GraphicsApi.OpenGL) {
|
||||
return 'essl';
|
||||
}
|
||||
else {
|
||||
throw new Error('Unsupported shader language.');
|
||||
}
|
||||
case Platform.HTML5:
|
||||
case Platform.DebugHTML5:
|
||||
case Platform.HTML5Worker:
|
||||
case Platform.Pi:
|
||||
return 'essl';
|
||||
case Platform.tvOS:
|
||||
case Platform.iOS:
|
||||
if (options.graphics === GraphicsApi.Metal || options.graphics === GraphicsApi.Default) {
|
||||
return 'metal';
|
||||
}
|
||||
else if (options.graphics === GraphicsApi.OpenGL) {
|
||||
return 'essl';
|
||||
}
|
||||
else {
|
||||
throw new Error('Unsupported shader language.');
|
||||
}
|
||||
case Platform.Windows:
|
||||
if (options.graphics === GraphicsApi.Vulkan) {
|
||||
return 'spirv';
|
||||
}
|
||||
else if (options.graphics === GraphicsApi.OpenGL) {
|
||||
return 'glsl';
|
||||
}
|
||||
else if (options.graphics === GraphicsApi.Direct3D11 || options.graphics === GraphicsApi.Direct3D12 || options.graphics === GraphicsApi.Default) {
|
||||
return 'd3d11';
|
||||
}
|
||||
else if (options.graphics === GraphicsApi.Direct3D9) {
|
||||
return 'd3d9';
|
||||
}
|
||||
else {
|
||||
throw new Error('Unsupported shader language.');
|
||||
}
|
||||
case Platform.WindowsApp:
|
||||
return 'd3d11';
|
||||
case Platform.Xbox360:
|
||||
case Platform.PlayStation3:
|
||||
return 'd3d9';
|
||||
case Platform.Linux:
|
||||
if (options.graphics === GraphicsApi.Vulkan || options.graphics === GraphicsApi.Default) {
|
||||
return 'spirv';
|
||||
}
|
||||
else if (options.graphics === GraphicsApi.OpenGL) {
|
||||
return 'glsl';
|
||||
}
|
||||
else {
|
||||
throw new Error('Unsupported shader language.');
|
||||
}
|
||||
case Platform.OSX:
|
||||
if (options.graphics === GraphicsApi.Metal || options.graphics === GraphicsApi.Default) {
|
||||
return 'metal';
|
||||
}
|
||||
else if (options.graphics === GraphicsApi.OpenGL) {
|
||||
return 'glsl';
|
||||
}
|
||||
else {
|
||||
throw new Error('Unsupported shader language.');
|
||||
}
|
||||
case Platform.Krom:
|
||||
if (options.graphics === GraphicsApi.Default) {
|
||||
if (process.platform === 'win32') {
|
||||
return 'd3d11';
|
||||
}
|
||||
else if (process.platform === 'darwin') {
|
||||
return 'metal';
|
||||
}
|
||||
else {
|
||||
return 'glsl';
|
||||
}
|
||||
}
|
||||
else if (options.graphics === GraphicsApi.Vulkan) {
|
||||
return 'spirv';
|
||||
}
|
||||
else if (options.graphics === GraphicsApi.Metal) {
|
||||
return 'metal';
|
||||
}
|
||||
else if (options.graphics === GraphicsApi.OpenGL) {
|
||||
return 'glsl';
|
||||
}
|
||||
else if (options.graphics === GraphicsApi.Direct3D11 || options.graphics === GraphicsApi.Direct3D12) {
|
||||
return 'd3d11';
|
||||
}
|
||||
else if (options.graphics === GraphicsApi.Direct3D9) {
|
||||
return 'd3d9';
|
||||
}
|
||||
else {
|
||||
throw new Error('Unsupported shader language.');
|
||||
}
|
||||
case Platform.FreeBSD:
|
||||
return 'glsl';
|
||||
default:
|
||||
return platform;
|
||||
}
|
||||
}
|
||||
|
||||
watch(watch: boolean, match: string, options: any, recompileAll: boolean) {
|
||||
return new Promise<CompiledShader[]>((resolve, reject) => {
|
||||
let shaders: string[] = [];
|
||||
let ready = false;
|
||||
this.watcher = chokidar.watch(match, { ignored: /[\/\\]\.(git|DS_Store)/, persistent: watch });
|
||||
this.watcher.on('add', (filepath: string) => {
|
||||
let file = path.parse(filepath);
|
||||
if (ready) {
|
||||
switch (file.ext) {
|
||||
case '.glsl':
|
||||
if (!file.name.endsWith('.inc') && this.isSupported(file.name)) {
|
||||
log.info('Compiling ' + file.name);
|
||||
this.compileShader(filepath, options, recompileAll);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (file.ext) {
|
||||
case '.glsl':
|
||||
if (!file.name.endsWith('.inc')) {
|
||||
shaders.push(filepath);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (watch) {
|
||||
this.watcher.on('change', (filepath: string) => {
|
||||
let file = path.parse(filepath);
|
||||
switch (file.ext) {
|
||||
case '.glsl':
|
||||
if (!file.name.endsWith('.inc') && this.isSupported(file.name)) {
|
||||
log.info('Recompiling ' + file.name);
|
||||
this.compileShader(filepath, options, recompileAll);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
this.watcher.on('unlink', (file: string) => {
|
||||
|
||||
});
|
||||
this.watcher.on('ready', async () => {
|
||||
ready = true;
|
||||
let compiledShaders: CompiledShader[] = [];
|
||||
|
||||
const self = this;
|
||||
async function compile(shader: any, index: number) {
|
||||
let parsed = path.parse(shader);
|
||||
if (self.isSupported(shader)) {
|
||||
log.info('Compiling shader ' + (index + 1) + ' of ' + shaders.length + ' (' + parsed.base + ').');
|
||||
let compiledShader: CompiledShader = null;
|
||||
try {
|
||||
compiledShader = await self.compileShader(shader, options, recompileAll);
|
||||
}
|
||||
catch (error) {
|
||||
log.error('Compiling shader ' + (index + 1) + ' of ' + shaders.length + ' (' + parsed.base + ') failed:');
|
||||
log.error(error);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
if (compiledShader === null) {
|
||||
compiledShader = new CompiledShader();
|
||||
compiledShader.noembed = options.noembed;
|
||||
// mark variables as invalid, so they are loaded from previous compilation
|
||||
compiledShader.files = null;
|
||||
compiledShader.inputs = null;
|
||||
compiledShader.outputs = null;
|
||||
compiledShader.uniforms = null;
|
||||
compiledShader.types = null;
|
||||
}
|
||||
if (compiledShader.files != null && compiledShader.files.length === 0) {
|
||||
// TODO: Remove when krafix has been recompiled everywhere
|
||||
compiledShader.files.push(parsed.name + '.' + self.type);
|
||||
}
|
||||
compiledShader.name = AssetConverter.createExportInfo(parsed, false, options, self.exporter.options.from).name;
|
||||
compiledShaders.push(compiledShader);
|
||||
}
|
||||
else {
|
||||
log.info('Skipping shader ' + (index + 1) + ' of ' + shaders.length + ' (' + parsed.base + ').');
|
||||
}
|
||||
++index;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (this.options.parallelAssetConversion !== 0) {
|
||||
let todo = shaders.map((shader, index) => {
|
||||
return async () => {
|
||||
await compile(shader, index);
|
||||
};
|
||||
});
|
||||
|
||||
let processes = this.options.parallelAssetConversion === -1
|
||||
? require('os').cpus().length - 1
|
||||
: this.options.parallelAssetConversion;
|
||||
|
||||
await Throttle.all(todo, {
|
||||
maxInProgress: processes,
|
||||
});
|
||||
}
|
||||
else {
|
||||
let index = 0;
|
||||
for (let shader of shaders) {
|
||||
try {
|
||||
await compile(shader, index);
|
||||
}
|
||||
catch (err) {
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
resolve(compiledShaders);
|
||||
return;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async run(watch: boolean, recompileAll: boolean): Promise<CompiledShader[]> {
|
||||
let shaders: CompiledShader[] = [];
|
||||
for (let matcher of this.shaderMatchers) {
|
||||
shaders = shaders.concat(await this.watch(watch, matcher.match, matcher.options, recompileAll));
|
||||
}
|
||||
return shaders;
|
||||
}
|
||||
|
||||
isSupported(file: string): boolean {
|
||||
if (file.endsWith('.frag.glsl') || file.endsWith('.vert.glsl')) {
|
||||
return true;
|
||||
}
|
||||
return this.type !== 'essl' && this.type !== 'agal';
|
||||
}
|
||||
|
||||
compileShader(file: string, options: any, recompile: boolean): Promise<CompiledShader> {
|
||||
return new Promise<CompiledShader>((resolve, reject) => {
|
||||
if (!this.compiler) reject('No shader compiler found.');
|
||||
|
||||
if (this.type === 'none') {
|
||||
resolve(new CompiledShader());
|
||||
return;
|
||||
}
|
||||
|
||||
let fileinfo = path.parse(file);
|
||||
let from = file;
|
||||
let to = path.join(this.to, fileinfo.name + '.' + this.type);
|
||||
let temp = to + '.temp';
|
||||
|
||||
fs.stat(from, (fromErr: NodeJS.ErrnoException, fromStats: fs.Stats) => {
|
||||
fs.stat(to, (toErr: NodeJS.ErrnoException, toStats: fs.Stats) => {
|
||||
if (options.noprocessing) {
|
||||
if (!toStats || toStats.mtime.getTime() < fromStats.mtime.getTime()) {
|
||||
fs.copySync(from, to, { overwrite: true });
|
||||
}
|
||||
let compiledShader = new CompiledShader();
|
||||
compiledShader.noembed = options.noembed;
|
||||
resolve(compiledShader);
|
||||
return;
|
||||
}
|
||||
fs.stat(this.compiler, (compErr: NodeJS.ErrnoException, compStats: fs.Stats) => {
|
||||
if (!recompile && (fromErr || (!toErr && toStats.mtime.getTime() > fromStats.mtime.getTime() && toStats.mtime.getTime() > compStats.mtime.getTime()))) {
|
||||
if (fromErr) log.error('Shader compiler error: ' + fromErr);
|
||||
resolve(null);
|
||||
}
|
||||
else {
|
||||
if (this.type === 'metal' && this.platform !== Platform.Krom) {
|
||||
fs.ensureDirSync(path.join(this.builddir, 'Sources'));
|
||||
let funcname = fileinfo.name;
|
||||
funcname = funcname.replace(/-/g, '_');
|
||||
funcname = funcname.replace(/\./g, '_');
|
||||
funcname += '_main';
|
||||
|
||||
fs.writeFileSync(to, '>' + funcname, 'utf8');
|
||||
|
||||
to = path.join(this.builddir, 'Sources', fileinfo.name + '.' + this.type);
|
||||
temp = to;
|
||||
}
|
||||
let parameters = [this.type === 'hlsl' ? 'd3d9' : this.type, from, temp, this.temp, this.platform];
|
||||
if (this.options.shaderversion) {
|
||||
parameters.push('--version');
|
||||
parameters.push(this.options.shaderversion);
|
||||
}
|
||||
else if (this.platform === Platform.Krom && os.platform() === 'linux') {
|
||||
parameters.push('--version');
|
||||
parameters.push('110');
|
||||
}
|
||||
if (this.options.glsl2) {
|
||||
parameters.push('--glsl2');
|
||||
}
|
||||
if (this.options.debug) {
|
||||
parameters.push('--debug');
|
||||
}
|
||||
if (options.defines) {
|
||||
for (let define of options.defines) {
|
||||
parameters.push('-D' + define);
|
||||
}
|
||||
}
|
||||
if (this.platform === Platform.HTML5 || this.platform === Platform.HTML5Worker || this.platform === Platform.Android) {
|
||||
parameters.push('--relax');
|
||||
}
|
||||
|
||||
parameters[1] = path.resolve(parameters[1]);
|
||||
parameters[2] = path.resolve(parameters[2]);
|
||||
parameters[3] = path.resolve(parameters[3]);
|
||||
|
||||
let child = child_process.spawn(this.compiler, parameters);
|
||||
|
||||
let errorLine = '';
|
||||
let newErrorLine = true;
|
||||
let errorData = false;
|
||||
|
||||
let compiledShader = new CompiledShader();
|
||||
compiledShader.noembed = options.noembed;
|
||||
|
||||
function parseData(data: string) {
|
||||
data = data.replace(':\\', '#\\'); // Filter out absolute paths on Windows
|
||||
let parts = data.split(':');
|
||||
if (parts.length >= 3) {
|
||||
if (parts[0] === 'uniform') {
|
||||
compiledShader.uniforms.push({name: parts[1], type: parts[2]});
|
||||
}
|
||||
else if (parts[0] === 'input') {
|
||||
compiledShader.inputs.push({name: parts[1], type: parts[2]});
|
||||
}
|
||||
else if (parts[0] === 'output') {
|
||||
compiledShader.outputs.push({name: parts[1], type: parts[2]});
|
||||
}
|
||||
else if (parts[0] === 'type') {
|
||||
let type = data.substring(data.indexOf(':') + 1);
|
||||
let name = type.substring(0, type.indexOf(':'));
|
||||
let typedata = type.substring(type.indexOf(':') + 2);
|
||||
typedata = typedata.substr(0, typedata.length - 1);
|
||||
let members = typedata.split(',');
|
||||
let memberdecls = [];
|
||||
for (let member of members) {
|
||||
let memberparts = member.split(':');
|
||||
memberdecls.push({type: memberparts[1], name: memberparts[0]});
|
||||
}
|
||||
compiledShader.types.push({name: name, members: memberdecls});
|
||||
}
|
||||
}
|
||||
else if (parts.length >= 2) {
|
||||
if (parts[0] === 'file') {
|
||||
const parsed = path.parse(parts[1].replace('#\\', ':\\'));
|
||||
let name = parsed.name;
|
||||
if (parsed.ext !== '.temp') name += parsed.ext;
|
||||
compiledShader.files.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let stdOutString = '';
|
||||
child.stdout.on('data', (data: any) => {
|
||||
stdOutString += data.toString();
|
||||
});
|
||||
|
||||
child.stderr.on('data', (data: any) => {
|
||||
let str: string = data.toString();
|
||||
for (let char of str) {
|
||||
if (char === '\n') {
|
||||
if (errorData) {
|
||||
parseData(errorLine.trim());
|
||||
}
|
||||
else {
|
||||
log.error(errorLine.trim());
|
||||
}
|
||||
errorLine = '';
|
||||
newErrorLine = true;
|
||||
errorData = false;
|
||||
}
|
||||
else if (newErrorLine && char === '#') {
|
||||
errorData = true;
|
||||
newErrorLine = false;
|
||||
}
|
||||
else {
|
||||
errorLine += char;
|
||||
newErrorLine = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
child.on('close', (code: number) => {
|
||||
if (stdOutString) {
|
||||
if (code === 0) {
|
||||
log.info(stdOutString);
|
||||
}
|
||||
else {
|
||||
log.error(stdOutString);
|
||||
}
|
||||
}
|
||||
|
||||
if (errorLine.trim().length > 0) {
|
||||
if (errorData) {
|
||||
parseData(errorLine.trim());
|
||||
}
|
||||
else {
|
||||
log.error(errorLine.trim());
|
||||
}
|
||||
}
|
||||
|
||||
if (code === 0) {
|
||||
if (this.type !== 'metal' || this.platform === Platform.Krom) {
|
||||
if (compiledShader.files === null || compiledShader.files.length === 0) {
|
||||
fs.renameSync(temp, to);
|
||||
}
|
||||
for (let file of compiledShader.files) {
|
||||
fs.renameSync(path.join(this.to, file + '.temp'), path.join(this.to, file));
|
||||
}
|
||||
}
|
||||
resolve(compiledShader);
|
||||
}
|
||||
else {
|
||||
process.exitCode = 1;
|
||||
reject('Shader compiler error.');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
9
Kha/Tools/khamake/src/VisualStudioVersion.ts
Normal file
9
Kha/Tools/khamake/src/VisualStudioVersion.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export const VisualStudioVersion = {
|
||||
VS2010: 'vs2010',
|
||||
VS2012: 'vs2012',
|
||||
VS2013: 'vs2013',
|
||||
VS2015: 'vs2015',
|
||||
VS2017: 'vs2017',
|
||||
VS2019: 'vs2019',
|
||||
VS2022: 'vs2022'
|
||||
};
|
7
Kha/Tools/khamake/src/VrApi.ts
Normal file
7
Kha/Tools/khamake/src/VrApi.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export let VrApi = {
|
||||
GearVr: 'gearvr',
|
||||
Cardboard: 'cardboard',
|
||||
Oculus: 'oculus',
|
||||
WebVR: 'webvr',
|
||||
None: 'none'
|
||||
};
|
48
Kha/Tools/khamake/src/XmlWriter.ts
Normal file
48
Kha/Tools/khamake/src/XmlWriter.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import * as fs from 'fs-extra';
|
||||
|
||||
function printElement(elem: any, data: string, indents: number) {
|
||||
for (let i = 0; i < indents; ++i) data += '\t';
|
||||
|
||||
if (typeof elem === 'string') {
|
||||
data += '<!-- ' + elem + ' -->\n';
|
||||
return data;
|
||||
}
|
||||
|
||||
data += '<' + elem.n;
|
||||
for (let a in elem) {
|
||||
if (a === 'n') continue;
|
||||
if (a === 'e') continue;
|
||||
data += ' ' + a + '="' + elem[a] + '"';
|
||||
}
|
||||
|
||||
if (elem.e === undefined || elem.e.length === 0) {
|
||||
data += ' />\n';
|
||||
}
|
||||
else {
|
||||
data += '>\n';
|
||||
for (let e of elem.e) {
|
||||
data = printElement(e, data, indents + 1);
|
||||
}
|
||||
for (let i = 0; i < indents; ++i) data += '\t';
|
||||
data += '</' + elem.n + '>\n';
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
export function writeXml(xml: any, path: string) {
|
||||
let data = '';
|
||||
data += '<?xml version="1.0" encoding="utf-8"?>\n';
|
||||
data += '<' + xml.n;
|
||||
for (let a in xml) {
|
||||
if (a === 'n') continue;
|
||||
if (a === 'e') continue;
|
||||
data += ' ' + a + '="' + xml[a] + '"';
|
||||
}
|
||||
data += '>\n';
|
||||
for (let e of xml.e) {
|
||||
data = printElement(e, data, 1);
|
||||
}
|
||||
data += '</' + xml.n + '>\n';
|
||||
fs.outputFileSync(path, data);
|
||||
}
|
37
Kha/Tools/khamake/src/defaults.ts
Normal file
37
Kha/Tools/khamake/src/defaults.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import {GraphicsApi} from './GraphicsApi';
|
||||
import {Platform} from './Platform';
|
||||
|
||||
export function graphicsApi(platform: string): string {
|
||||
switch (platform) {
|
||||
case Platform.Empty:
|
||||
case Platform.Node:
|
||||
case Platform.Android:
|
||||
case Platform.HTML5:
|
||||
case Platform.DebugHTML5:
|
||||
case Platform.HTML5Worker:
|
||||
case Platform.Pi:
|
||||
case Platform.Linux:
|
||||
return GraphicsApi.OpenGL;
|
||||
case Platform.tvOS:
|
||||
case Platform.iOS:
|
||||
case Platform.OSX:
|
||||
return GraphicsApi.Metal;
|
||||
case Platform.Windows:
|
||||
case Platform.WindowsApp:
|
||||
return GraphicsApi.Direct3D11;
|
||||
case Platform.Krom:
|
||||
if (process.platform === 'win32') {
|
||||
return GraphicsApi.Direct3D11;
|
||||
}
|
||||
else if (process.platform === 'darwin') {
|
||||
return GraphicsApi.Metal;
|
||||
}
|
||||
else {
|
||||
return GraphicsApi.OpenGL;
|
||||
}
|
||||
case Platform.FreeBSD:
|
||||
return GraphicsApi.OpenGL;
|
||||
default:
|
||||
return platform;
|
||||
}
|
||||
}
|
28
Kha/Tools/khamake/src/exec.ts
Normal file
28
Kha/Tools/khamake/src/exec.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import * as os from 'os';
|
||||
|
||||
export function sys() {
|
||||
if (os.platform() === 'win32') {
|
||||
return '.exe';
|
||||
}
|
||||
else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function sysdir() {
|
||||
if (os.platform() === 'linux') {
|
||||
if (os.arch() === 'arm') return 'linux_arm';
|
||||
if (os.arch() === 'arm64') return 'linux_arm64';
|
||||
else if (os.arch() === 'x64') return 'linux_x64';
|
||||
else throw 'Unsupported CPU';
|
||||
}
|
||||
else if (os.platform() === 'win32') {
|
||||
return 'windows_x64';
|
||||
}
|
||||
else if (os.platform() === 'freebsd') {
|
||||
return 'freebsd_x64';
|
||||
}
|
||||
else {
|
||||
return 'macos';
|
||||
}
|
||||
}
|
75
Kha/Tools/khamake/src/init.ts
Normal file
75
Kha/Tools/khamake/src/init.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
export function run(name: string, from: string, projectfile: string) {
|
||||
if (!fs.existsSync(path.join(from, projectfile))) {
|
||||
fs.writeFileSync(path.join(from, projectfile),
|
||||
'let project = new Project(\'New Project\');\n'
|
||||
+ 'project.addAssets(\'Assets/**\');\n'
|
||||
+ 'project.addShaders(\'Shaders/**\');\n'
|
||||
+ 'project.addSources(\'Sources\');\n'
|
||||
+ 'resolve(project);\n',
|
||||
{ encoding: 'utf8' });
|
||||
}
|
||||
|
||||
if (!fs.existsSync(path.join(from, 'Assets'))) fs.mkdirSync(path.join(from, 'Assets'));
|
||||
if (!fs.existsSync(path.join(from, 'Shaders'))) fs.mkdirSync(path.join(from, 'Shaders'));
|
||||
if (!fs.existsSync(path.join(from, 'Sources'))) fs.mkdirSync(path.join(from, 'Sources'));
|
||||
|
||||
let friendlyName = name;
|
||||
friendlyName = friendlyName.replace(/ /g, '_');
|
||||
friendlyName = friendlyName.replace(/-/g, '_');
|
||||
|
||||
if (!fs.existsSync(path.join(from, 'Sources', 'Main.hx'))) {
|
||||
let mainsource =
|
||||
'package;\n\n'
|
||||
+ 'import kha.Assets;\n'
|
||||
+ 'import kha.Color;\n'
|
||||
+ 'import kha.Framebuffer;\n'
|
||||
+ 'import kha.Scheduler;\n'
|
||||
+ 'import kha.System;\n\n'
|
||||
+ 'class Main {\n'
|
||||
+ '\tstatic var logo = ["1 1 1 1 111", "11 111 111", "1 1 1 1 1 1"];\n\n'
|
||||
+ '\tstatic function update(): Void {\n'
|
||||
+ '\t}\n\n'
|
||||
+ '\tstatic function render(frames: Array<Framebuffer>): Void {\n'
|
||||
+ '\t\t// As we are using only 1 window, grab the first framebuffer\n'
|
||||
+ '\t\tfinal fb = frames[0];\n'
|
||||
+ '\t\t// Now get the `g2` graphics object so we can draw\n'
|
||||
+ '\t\tfinal g2 = fb.g2;\n'
|
||||
+ '\t\t// Start drawing, and clear the framebuffer to `petrol`\n'
|
||||
+ '\t\tg2.begin(true, Color.fromBytes(0, 95, 106));\n'
|
||||
+ '\t\t// Offset all following drawing operations from the top-left a bit\n'
|
||||
+ '\t\tg2.pushTranslation(64, 64);\n'
|
||||
+ '\t\t// Fill the following rects with red\n'
|
||||
+ '\t\tg2.color = Color.Red;\n\n'
|
||||
+ '\t\t// Loop over the logo (Array<String>) and draw a rect for each "1"\n'
|
||||
+ '\t\tfor (rowIndex in 0...logo.length) {\n'
|
||||
+ '\t\t final row = logo[rowIndex];\n\n'
|
||||
+ '\t\t for (colIndex in 0...row.length) {\n'
|
||||
+ '\t\t switch row.charAt(colIndex) {\n'
|
||||
+ '\t\t case "1": g2.fillRect(colIndex * 16, rowIndex * 16, 16, 16);\n'
|
||||
+ '\t\t case _:\n'
|
||||
+ '\t\t }\n'
|
||||
+ '\t\t }\n'
|
||||
+ '\t\t}\n\n'
|
||||
+ '\t\t// Pop the pushed translation so it will not accumulate over multiple frames\n'
|
||||
+ '\t\tg2.popTransformation();\n'
|
||||
+ '\t\t// Finish the drawing operations\n'
|
||||
+ '\t\tg2.end();\n'
|
||||
+ '\t}\n\n'
|
||||
+ '\tpublic static function main() {\n'
|
||||
+ '\t\tSystem.start({title: "' + name + '", width: 1024, height: 768}, function (_) {\n'
|
||||
+ '\t\t\t// Just loading everything is ok for small projects\n'
|
||||
+ '\t\t\tAssets.loadEverything(function () {\n'
|
||||
+ '\t\t\t\t// Avoid passing update/render directly,\n'
|
||||
+ '\t\t\t\t// so replacing them via code injection works\n'
|
||||
+ '\t\t\t\tScheduler.addTimeTask(function () { update(); }, 0, 1 / 60);\n'
|
||||
+ '\t\t\t\tSystem.notifyOnFrames(function (frames) { render(frames); });\n'
|
||||
+ '\t\t\t});\n'
|
||||
+ '\t\t});\n'
|
||||
+ '\t}\n'
|
||||
+ '}\n';
|
||||
fs.writeFileSync(path.join(from, 'Sources', 'Main.hx'), mainsource, { encoding: 'utf8' });
|
||||
}
|
||||
}
|
437
Kha/Tools/khamake/src/khamake.ts
Normal file
437
Kha/Tools/khamake/src/khamake.ts
Normal file
@ -0,0 +1,437 @@
|
||||
// Called from entry point, e.g. Kha/make.js
|
||||
// This is where options are processed:
|
||||
// e.g. '-t html5 --server'
|
||||
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import {Callbacks} from './ProjectFile';
|
||||
import {GraphicsApi} from './GraphicsApi';
|
||||
import {Architecture} from './Architecture';
|
||||
import {AudioApi} from './AudioApi';
|
||||
import {VrApi} from './VrApi';
|
||||
import {RayTraceApi} from './RayTraceApi';
|
||||
import {Options} from './Options';
|
||||
import {Platform} from './Platform';
|
||||
import {VisualStudioVersion} from './VisualStudioVersion';
|
||||
|
||||
let defaultTarget: string;
|
||||
if (os.platform() === 'linux') {
|
||||
defaultTarget = Platform.Linux;
|
||||
}
|
||||
else if (os.platform() === 'win32') {
|
||||
defaultTarget = Platform.Windows;
|
||||
}
|
||||
else if (os.platform() === 'freebsd') {
|
||||
defaultTarget = Platform.FreeBSD;
|
||||
}
|
||||
else {
|
||||
defaultTarget = Platform.OSX;
|
||||
}
|
||||
|
||||
let options: Array<any> = [
|
||||
{
|
||||
full: 'from',
|
||||
value: true,
|
||||
description: 'Location of your project',
|
||||
default: '.'
|
||||
},
|
||||
{
|
||||
full: 'to',
|
||||
value: true,
|
||||
description: 'Build location',
|
||||
default: 'build'
|
||||
},
|
||||
{
|
||||
full: 'projectfile',
|
||||
value: true,
|
||||
description: 'Name of your project file, defaults to "khafile.js"',
|
||||
default: 'khafile.js'
|
||||
},
|
||||
{
|
||||
full: 'target',
|
||||
short: 't',
|
||||
value: true,
|
||||
description: 'Target platform',
|
||||
default: defaultTarget
|
||||
},
|
||||
{
|
||||
full: 'vr',
|
||||
value: true,
|
||||
description: 'Target VR device',
|
||||
default: VrApi.None
|
||||
},
|
||||
{
|
||||
full: 'raytrace',
|
||||
value: true,
|
||||
description: 'Target raytracing api',
|
||||
default: RayTraceApi.None
|
||||
},
|
||||
{
|
||||
full: 'main',
|
||||
value: true,
|
||||
description: 'Entrypoint for the haxe code (-main argument), defaults to "Main".',
|
||||
default: 'Main'
|
||||
},
|
||||
{
|
||||
full: 'intermediate',
|
||||
description: 'Intermediate location for object files.',
|
||||
value: true,
|
||||
default: '',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
full: 'graphics',
|
||||
short: 'g',
|
||||
description: 'Graphics api to use. Possible parameters are direct3d9, direct3d11, direct3d12, metal, vulkan and opengl.',
|
||||
value: true,
|
||||
default: GraphicsApi.Default
|
||||
},
|
||||
{
|
||||
full: 'arch',
|
||||
description: 'Target architecture to use. Possible parameters are arm7, arm8, x86, x86_64.',
|
||||
value: true,
|
||||
default: Architecture.Default
|
||||
},
|
||||
{
|
||||
full: 'audio',
|
||||
short: 'a',
|
||||
description: 'Audio api to use. Possible parameters are directsound and wasapi.',
|
||||
value: true,
|
||||
default: AudioApi.Default
|
||||
},
|
||||
{
|
||||
full: 'visualstudio',
|
||||
short: 'v',
|
||||
description: 'Version of Visual Studio to use. Possible parameters are vs2010, vs2012, vs2013, vs2015, vs2017, vs2019 and vs2022.',
|
||||
value: true,
|
||||
default: VisualStudioVersion.VS2022
|
||||
},
|
||||
{
|
||||
full: 'kha',
|
||||
short: 'k',
|
||||
description: 'Location of Kha directory',
|
||||
value: true,
|
||||
default: ''
|
||||
},
|
||||
{
|
||||
full: 'haxe',
|
||||
description: 'Location of Haxe directory',
|
||||
value: true,
|
||||
default: ''
|
||||
},
|
||||
{
|
||||
full: 'nohaxe',
|
||||
description: 'Do not compile Haxe sources',
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
full: 'ffmpeg',
|
||||
description: 'Location of ffmpeg executable',
|
||||
value: true,
|
||||
default: ''
|
||||
},
|
||||
{
|
||||
full: 'ogg',
|
||||
description: 'Commandline for running the ogg encoder',
|
||||
value: true,
|
||||
default: ''
|
||||
},
|
||||
{
|
||||
full: 'mp3',
|
||||
description: 'Commandline for running the mp3 encoder',
|
||||
value: true,
|
||||
default: ''
|
||||
},
|
||||
{
|
||||
full: 'aac',
|
||||
description: 'Commandline for running the ffmpeg executable',
|
||||
value: true,
|
||||
default: ''
|
||||
},
|
||||
{
|
||||
full: 'krafix',
|
||||
description: 'Location of krafix shader compiler',
|
||||
value: true,
|
||||
default: ''
|
||||
},
|
||||
{
|
||||
full: 'kraffiti',
|
||||
description: 'Location of kraffiti image processing tool',
|
||||
value: true,
|
||||
default: ''
|
||||
},
|
||||
{
|
||||
full: 'noshaders',
|
||||
description: 'Do not compile shaders',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
full: 'noproject',
|
||||
description: 'Only source files. Don\'t generate project files.',
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
full: 'onlydata',
|
||||
description: 'Only assets/data. Don\'t generate project files.',
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
full: 'embedflashassets',
|
||||
description: 'Embed assets in swf for flash target',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
full: 'compile',
|
||||
description: 'Compile executable',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
full: 'run',
|
||||
description: 'Run executable',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
full: 'init',
|
||||
description: 'Init a Kha project inside the current directory',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
full: 'name',
|
||||
description: 'Project name to use when initializing a project',
|
||||
value: true,
|
||||
default: 'Project'
|
||||
},
|
||||
{
|
||||
full: 'server',
|
||||
description: 'Run local http server for html5 target',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
full: 'port',
|
||||
description: 'Running port for the server',
|
||||
value: true,
|
||||
default: 8080
|
||||
},
|
||||
{
|
||||
full: 'debug',
|
||||
description: 'Compile in debug mode.',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
full: 'silent',
|
||||
description: 'Silent mode.',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
full: 'quiet',
|
||||
description: 'Quiet mode. Like silent mode but prints error messages.',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
full: 'watch',
|
||||
short: 'w',
|
||||
description: 'Watch files and recompile on change.',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
full: 'watchport',
|
||||
short: 'wp',
|
||||
description: 'Port for the compilation server (default value is 7000).',
|
||||
value: true,
|
||||
default: '7000',
|
||||
},
|
||||
{
|
||||
full: 'livereload',
|
||||
description: 'Reload http server page on watch mode recompilations.',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
full: 'glsl2',
|
||||
description: 'Use experimental SPIRV-Cross glsl mode.',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
full: 'shaderversion',
|
||||
description: 'Set target shader version manually.',
|
||||
value: true,
|
||||
default: null
|
||||
},
|
||||
{
|
||||
full: 'parallelAssetConversion',
|
||||
description: 'Experimental - Spawn multiple processes during asset and shader conversion. Possible values:\n 0: disabled (default value)\n -1: choose number of processes automatically\n N: specify number of processes manually',
|
||||
value: true,
|
||||
default: 0
|
||||
},
|
||||
{
|
||||
full: 'slowgc',
|
||||
description: 'Disables generational garbage collection.',
|
||||
value: false
|
||||
},
|
||||
{
|
||||
full: 'nosigning',
|
||||
value: false,
|
||||
description: 'Disable code signing for iOS'
|
||||
}
|
||||
];
|
||||
|
||||
let parsedOptions: any = new Options();
|
||||
|
||||
function printHelpLine(options: String, description: String) {
|
||||
let helpLine = options;
|
||||
while (helpLine.length < 30) {
|
||||
helpLine += ' ';
|
||||
}
|
||||
helpLine += '' + description;
|
||||
console.log(helpLine);
|
||||
console.log();
|
||||
}
|
||||
|
||||
function printHelp() {
|
||||
console.log('khamake options:\n');
|
||||
for (let option of options) {
|
||||
if (option.hidden) {
|
||||
continue;
|
||||
}
|
||||
if (option.short) {
|
||||
printHelpLine('-' + option.short + ' ' + '--' + option.full, option.description);
|
||||
}
|
||||
else {
|
||||
printHelpLine('--' + option.full, option.description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isTarget(target: string) {
|
||||
if (target.trim().length < 1) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (let option of options) {
|
||||
if (option.value) {
|
||||
parsedOptions[option.full] = option.default;
|
||||
}
|
||||
else {
|
||||
parsedOptions[option.full] = false;
|
||||
}
|
||||
}
|
||||
|
||||
let targetIsDefault = true;
|
||||
|
||||
let args = process.argv;
|
||||
for (let i = 2; i < args.length; ++i) {
|
||||
let arg = args[i];
|
||||
if (arg === '--') break;
|
||||
|
||||
if (arg[0] === '-') {
|
||||
if (arg[1] === '-') {
|
||||
if (arg.substr(2) === 'help') {
|
||||
printHelp();
|
||||
process.exit(0);
|
||||
}
|
||||
for (let option of options) {
|
||||
if (arg.substr(2) === option.full) {
|
||||
if (option.value) {
|
||||
++i;
|
||||
parsedOptions[option.full] = args[i];
|
||||
if (option.full === 'target') {
|
||||
targetIsDefault = false;
|
||||
}
|
||||
else if (option.full === 'parallelAssetConversion') {
|
||||
parsedOptions[option.full] = parseInt(parsedOptions[option.full]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
parsedOptions[option.full] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (arg[1] === 'h') {
|
||||
printHelp();
|
||||
process.exit(0);
|
||||
}
|
||||
for (let option of options) {
|
||||
if (option.short && arg[1] === option.short) {
|
||||
if (option.value) {
|
||||
++i;
|
||||
parsedOptions[option.full] = args[i];
|
||||
if (option.full === 'target') {
|
||||
targetIsDefault = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
parsedOptions[option.full] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (isTarget(arg)) {
|
||||
parsedOptions.target = arg.toLowerCase();
|
||||
targetIsDefault = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parsedOptions.run) {
|
||||
parsedOptions.compile = true;
|
||||
}
|
||||
|
||||
async function runKhamake() {
|
||||
try {
|
||||
let logInfo = function (text: string, newline: boolean) {
|
||||
if (newline) {
|
||||
console.log(text);
|
||||
}
|
||||
else {
|
||||
process.stdout.write(text);
|
||||
}
|
||||
};
|
||||
|
||||
let logError = function (text: string, newline: boolean) {
|
||||
if (newline) {
|
||||
console.error(text);
|
||||
}
|
||||
else {
|
||||
process.stderr.write(text);
|
||||
}
|
||||
};
|
||||
await require('./main.js').run(parsedOptions, { info: logInfo, error: logError });
|
||||
}
|
||||
catch (error) {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (parsedOptions.init) {
|
||||
console.log('Initializing Kha project.\n');
|
||||
require('./init').run(parsedOptions.name, parsedOptions.from, parsedOptions.projectfile);
|
||||
console.log('If you want to use the git version of Kha, execute "git init" and "git submodule add https://github.com/Kode/Kha.git".');
|
||||
}
|
||||
else if (parsedOptions.server) {
|
||||
console.log('Running server on ' + parsedOptions.port);
|
||||
let nstatic = require('node-static');
|
||||
let fileServer = new nstatic.Server(path.join(parsedOptions.from, 'build', targetIsDefault ? 'html5' : parsedOptions.target), { cache: 0 });
|
||||
let server = require('http').createServer(function (request: any, response: any) {
|
||||
request.addListener('end', function () {
|
||||
fileServer.serve(request, response);
|
||||
}).resume();
|
||||
});
|
||||
server.on('error', function (e: any) {
|
||||
if (e.code === 'EADDRINUSE') {
|
||||
console.log('Error: Port ' + parsedOptions.port + ' is already in use.');
|
||||
console.log('Please close the competing program (maybe another instance of khamake?)');
|
||||
console.log('or switch to a different port using the --port argument.');
|
||||
}
|
||||
});
|
||||
server.listen(parsedOptions.port);
|
||||
if (parsedOptions.watch) runKhamake();
|
||||
}
|
||||
else {
|
||||
runKhamake();
|
||||
}
|
12
Kha/Tools/khamake/src/korepath.ts
Normal file
12
Kha/Tools/khamake/src/korepath.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import * as path from 'path';
|
||||
import {sysdir} from './exec';
|
||||
|
||||
let korepath = path.join(__dirname, '..', '..', '..', 'Kinc', 'Tools', sysdir());
|
||||
|
||||
export function init(options: any) {
|
||||
korepath = path.join(options.kha, 'Kinc', 'Tools', sysdir());
|
||||
}
|
||||
|
||||
export function get() {
|
||||
return korepath;
|
||||
}
|
37
Kha/Tools/khamake/src/log.ts
Normal file
37
Kha/Tools/khamake/src/log.ts
Normal file
@ -0,0 +1,37 @@
|
||||
let myInfo = function (text: string, newline: boolean) {
|
||||
if (newline) {
|
||||
console.log(text);
|
||||
}
|
||||
else {
|
||||
process.stdout.write(text);
|
||||
}
|
||||
};
|
||||
|
||||
let myError = function (text: string, newline: boolean) {
|
||||
if (newline) {
|
||||
console.error(text);
|
||||
}
|
||||
else {
|
||||
process.stderr.write(text);
|
||||
}
|
||||
};
|
||||
|
||||
export function set(log: {info: (text: string, newline: boolean) => void, error: (text: string, newline: boolean) => void}) {
|
||||
myInfo = log.info;
|
||||
myError = log.error;
|
||||
}
|
||||
|
||||
export function silent(showErrors: boolean = false) {
|
||||
myInfo = function () {};
|
||||
if (!showErrors) {
|
||||
myError = function () {};
|
||||
}
|
||||
}
|
||||
|
||||
export function info(text: string, newline: boolean = true) {
|
||||
myInfo(text, newline);
|
||||
}
|
||||
|
||||
export function error(text: string, newline: boolean = true) {
|
||||
myError(text, newline);
|
||||
}
|
799
Kha/Tools/khamake/src/main.ts
Normal file
799
Kha/Tools/khamake/src/main.ts
Normal file
@ -0,0 +1,799 @@
|
||||
import * as child_process from 'child_process';
|
||||
import * as fs from 'fs-extra';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
|
||||
import {sys, sysdir} from './exec';
|
||||
import * as korepath from './korepath';
|
||||
import * as log from './log';
|
||||
import {Options} from './Options';
|
||||
import {Platform} from './Platform';
|
||||
import {Project, Target, Library} from './Project';
|
||||
import {loadProject, Callbacks} from './ProjectFile';
|
||||
import {VisualStudioVersion} from './VisualStudioVersion';
|
||||
import {AssetConverter} from './AssetConverter';
|
||||
import {HaxeCompiler} from './HaxeCompiler';
|
||||
import {ShaderCompiler, CompiledShader} from './ShaderCompiler';
|
||||
import {KhaExporter} from './Exporters/KhaExporter';
|
||||
import {DebugHtml5Exporter} from './Exporters/DebugHtml5Exporter';
|
||||
import {EmptyExporter} from './Exporters/EmptyExporter';
|
||||
import {FlashExporter} from './Exporters/FlashExporter';
|
||||
import {Html5Exporter} from './Exporters/Html5Exporter';
|
||||
import {Html5WorkerExporter} from './Exporters/Html5WorkerExporter';
|
||||
import {JavaExporter} from './Exporters/JavaExporter';
|
||||
import {KincExporter} from './Exporters/KincExporter';
|
||||
import {KincHLExporter} from './Exporters/KincHLExporter';
|
||||
import {KromExporter} from './Exporters/KromExporter';
|
||||
import {NodeExporter} from './Exporters/NodeExporter';
|
||||
import {PlayStationMobileExporter} from './Exporters/PlayStationMobileExporter';
|
||||
import {WpfExporter} from './Exporters/WpfExporter';
|
||||
import {writeHaxeProject} from './HaxeProject';
|
||||
import * as Icon from './Icon';
|
||||
|
||||
let lastAssetConverter: AssetConverter;
|
||||
let lastShaderCompiler: ShaderCompiler;
|
||||
let lastHaxeCompiler: HaxeCompiler;
|
||||
|
||||
function fixName(name: string): string {
|
||||
name = name.replace(/[-@\ \.\/\\]/g, '_');
|
||||
if (name[0] === '0' || name[0] === '1' || name[0] === '2' || name[0] === '3' || name[0] === '4'
|
||||
|| name[0] === '5' || name[0] === '6' || name[0] === '7' || name[0] === '8' || name[0] === '9') {
|
||||
name = '_' + name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
function safeName(name: string): string {
|
||||
return name.replace(/[^A-z0-9\-\_]/g, '-');
|
||||
}
|
||||
|
||||
function createKorefile(name: string, exporter: KhaExporter, options: Options, targetOptions: any, libraries: Library[], cdefines: string[], cflags: string[], cppflags: string[], stackSize: number, version: string, id: string, korehl: boolean, icon: string): string {
|
||||
let out = '';
|
||||
out += 'let fs = require(\'fs\');\n';
|
||||
out += 'let path = require(\'path\');\n';
|
||||
out += 'let project = new Project(\'' + name + '\');\n';
|
||||
if (version) {
|
||||
out += 'project.version = \'' + version + '\';\n';
|
||||
}
|
||||
if (id) {
|
||||
out += 'project.id = \'' + id + '\';\n';
|
||||
}
|
||||
if (icon != null) out += 'project.icon = \'' + icon + '\';\n';
|
||||
|
||||
for (let cdefine of cdefines) {
|
||||
out += 'project.addDefine(\'' + cdefine + '\');\n';
|
||||
}
|
||||
|
||||
for (let cppflag of cppflags) {
|
||||
out += 'project.addCppFlag(\'' + cppflag + '\');\n';
|
||||
}
|
||||
|
||||
for (let cflag of cflags) {
|
||||
out += 'project.addCFlag(\'' + cflag + '\');\n';
|
||||
}
|
||||
|
||||
out += 'project.addDefine(\'HXCPP_API_LEVEL=400\');\n';
|
||||
out += 'project.addDefine(\'HXCPP_DEBUG\', \'Debug\');\n';
|
||||
if (!options.slowgc) {
|
||||
out += 'project.addDefine(\'HXCPP_GC_GENERATIONAL\');\n';
|
||||
}
|
||||
|
||||
if (targetOptions) {
|
||||
let koreTargetOptions: any = {};
|
||||
for (let option in targetOptions) {
|
||||
koreTargetOptions[option] = targetOptions[option];
|
||||
}
|
||||
out += 'project.targetOptions = ' + JSON.stringify(koreTargetOptions) + ';\n';
|
||||
}
|
||||
|
||||
out += 'project.setDebugDir(\'' + path.relative(options.from, path.join(options.to, exporter.sysdir())).replace(/\\/g, '/') + '\');\n';
|
||||
|
||||
let buildpath = path.relative(options.from, path.join(options.to, exporter.sysdir() + '-build')).replace(/\\/g, '/');
|
||||
if (buildpath.startsWith('..')) buildpath = path.resolve(path.join(options.from.toString(), buildpath));
|
||||
|
||||
out += 'await project.addProject(\'' + path.join(options.kha, 'Kinc').replace(/\\/g, '/') + '\');\n';
|
||||
out += 'await project.addProject(\'' + buildpath.replace(/\\/g, '/') + '\');\n';
|
||||
if (korehl) out += 'await project.addProject(\'' + path.join(options.kha, 'Backends', 'Kinc-HL').replace(/\\/g, '/') + '\');\n';
|
||||
else out += 'await project.addProject(\'' + path.join(options.kha, 'Backends', 'Kinc-hxcpp').replace(/\\/g, '/') + '\');\n';
|
||||
|
||||
for (let lib of libraries) {
|
||||
let libPath: string = lib.libpath.replace(/\\/g, '/');
|
||||
out += 'if (fs.existsSync(path.join(\'' + libPath + '\', \'kfile.js\')) || fs.existsSync(path.join(\'' + libPath + '\', \'kincfile.js\')) || fs.existsSync(path.join(\'' + libPath + '\', \'korefile.js\'))) {\n';
|
||||
out += '\tawait project.addProject(\'' + libPath + '\');\n';
|
||||
out += '}\n';
|
||||
}
|
||||
|
||||
if (stackSize) {
|
||||
out += 'project.stackSize = ' + stackSize + ';\n';
|
||||
}
|
||||
|
||||
out += 'project.flatten();\n';
|
||||
|
||||
out += 'resolve(project);\n';
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function runKmake(options: string[]) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const child = child_process.spawn(path.join(korepath.get(), 'kmake' + sys()), options);
|
||||
|
||||
child.stdout.on('data', (data: any) => {
|
||||
const str = data.toString();
|
||||
log.info(str, false);
|
||||
});
|
||||
|
||||
child.stderr.on('data', (data: any) => {
|
||||
const str = data.toString();
|
||||
log.error(str, false);
|
||||
});
|
||||
|
||||
child.on('error', (err: any) => {
|
||||
log.error('Could not start kmake.');
|
||||
reject();
|
||||
});
|
||||
|
||||
child.on('close', (code: number) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
}
|
||||
else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function exportProjectFiles(name: string, resourceDir: string, options: Options, exporter: KhaExporter, kore: boolean, korehl: boolean, icon: string,
|
||||
libraries: Library[], targetOptions: any, defines: string[], cdefines: string[], cflags: string[], cppflags: string[], stackSize: number, version: string, id: string): Promise<string> {
|
||||
if (options.haxe !== '') {
|
||||
let haxeOptions = exporter.haxeOptions(name, targetOptions, defines);
|
||||
haxeOptions.defines.push('kha');
|
||||
haxeOptions.defines.push('kha_version=1810');
|
||||
haxeOptions.safeName = safeName(haxeOptions.name);
|
||||
haxeOptions.defines.push('kha_project_name=' + haxeOptions.name);
|
||||
if (options.livereload) haxeOptions.defines.push('kha_live_reload');
|
||||
|
||||
if (options.debug && haxeOptions.parameters.indexOf('-debug') < 0) {
|
||||
haxeOptions.parameters.push('-debug');
|
||||
}
|
||||
|
||||
writeHaxeProject(options.to, !options.noproject, haxeOptions);
|
||||
|
||||
if (!options.nohaxe) {
|
||||
let compiler = new HaxeCompiler(options.to, haxeOptions.to, haxeOptions.realto, resourceDir, options.haxe, 'project-' + exporter.sysdir() + '.hxml', haxeOptions.sources, exporter.sysdir(), options.watchport, options.livereload, options.port);
|
||||
lastHaxeCompiler = compiler;
|
||||
try {
|
||||
await compiler.run(options.watch);
|
||||
}
|
||||
catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
for (let callback of Callbacks.postHaxeCompilation) {
|
||||
callback();
|
||||
}
|
||||
|
||||
await exporter.export(name, targetOptions, haxeOptions);
|
||||
}
|
||||
|
||||
let buildDir = path.join(options.to, exporter.sysdir() + '-build');
|
||||
|
||||
if (options.haxe !== '' && kore && !options.noproject) {
|
||||
// If target is a Kore project, generate additional project folders here.
|
||||
// generate the kincfile.js
|
||||
fs.copySync(path.join(__dirname, '..', 'Data', 'hxcpp', 'kfile.js'), path.join(buildDir, 'kfile.js'), { overwrite: true });
|
||||
fs.writeFileSync(path.join(options.to, 'kfile.js'), createKorefile(name, exporter, options, targetOptions, libraries, cdefines, cflags, cppflags, stackSize, version, id, false, icon));
|
||||
|
||||
// Similar to khamake.js -> main.js -> run(...)
|
||||
// We now do kincmake.js -> main.js -> run(...)
|
||||
// This will create additional project folders for the target,
|
||||
// e.g. 'build/pi-build'
|
||||
try {
|
||||
const kmakeOptions = ['--from', options.from, '--to', buildDir, '--kfile', path.resolve(options.to, 'kfile.js'), '-t', koreplatform(options.target), '--noshaders',
|
||||
'--graphics', options.graphics, '--arch', options.arch, '--audio', options.audio, '--vr', options.vr, '-v', options.visualstudio
|
||||
];
|
||||
if (options.nosigning) {
|
||||
kmakeOptions.push('--nosigning');
|
||||
}
|
||||
if (options.debug) {
|
||||
kmakeOptions.push('--debug');
|
||||
}
|
||||
if (options.run) {
|
||||
kmakeOptions.push('--run');
|
||||
}
|
||||
if (options.compile) {
|
||||
kmakeOptions.push('--compile');
|
||||
}
|
||||
await runKmake(kmakeOptions);
|
||||
for (let callback of Callbacks.postCppCompilation) {
|
||||
callback();
|
||||
}
|
||||
log.info('Done.');
|
||||
return name;
|
||||
}
|
||||
catch (error) {
|
||||
if (error) {
|
||||
log.error('Error: ' + error);
|
||||
}
|
||||
else {
|
||||
log.error('Error.');
|
||||
}
|
||||
process.exit(1);
|
||||
return name;
|
||||
}
|
||||
}
|
||||
else if (options.haxe !== '' && korehl && !options.noproject) {
|
||||
fs.copySync(path.join(__dirname, '..', 'Data', 'hl', 'kore_sources.c'), path.join(buildDir, 'kore_sources.c'), { overwrite: true });
|
||||
fs.copySync(path.join(__dirname, '..', 'Data', 'hl', 'kfile.js'), path.join(buildDir, 'kfile.js'), { overwrite: true });
|
||||
fs.writeFileSync(path.join(options.to, 'kfile.js'), createKorefile(name, exporter, options, targetOptions, libraries, cdefines, cflags, cppflags, stackSize, version, id, korehl, icon));
|
||||
|
||||
try {
|
||||
const kmakeOptions = ['--from', options.from, '--to', buildDir, '--kfile', path.resolve(options.to, 'kfile.js'), '-t', koreplatform(options.target), '--noshaders',
|
||||
'--graphics', options.graphics, '--arch', options.arch, '--audio', options.audio, '--vr', options.vr, '-v', options.visualstudio
|
||||
];
|
||||
if (options.nosigning) {
|
||||
kmakeOptions.push('--nosigning');
|
||||
}
|
||||
if (options.debug) {
|
||||
kmakeOptions.push('--debug');
|
||||
}
|
||||
if (options.run) {
|
||||
kmakeOptions.push('--run');
|
||||
}
|
||||
if (options.compile) {
|
||||
kmakeOptions.push('--compile');
|
||||
}
|
||||
await runKmake(kmakeOptions);
|
||||
for (let callback of Callbacks.postCppCompilation) {
|
||||
callback();
|
||||
}
|
||||
log.info('Done.');
|
||||
return name;
|
||||
}
|
||||
catch (error) {
|
||||
if (error) {
|
||||
log.error('Error: ' + error);
|
||||
}
|
||||
else {
|
||||
log.error('Error.');
|
||||
}
|
||||
process.exit(1);
|
||||
return name;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If target is not a Kore project, e.g. HTML5, finish building here.
|
||||
log.info('Done.');
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
function checkKorePlatform(platform: string) {
|
||||
return platform === 'windows'
|
||||
|| platform === 'windowsapp'
|
||||
|| platform === 'ios'
|
||||
|| platform === 'osx'
|
||||
|| platform === 'android'
|
||||
|| platform === 'linux'
|
||||
|| platform === 'emscripten'
|
||||
|| platform === 'pi'
|
||||
|| platform === 'tvos'
|
||||
|| platform === 'ps4'
|
||||
|| platform === 'xboxone'
|
||||
|| platform === 'switch'
|
||||
|| platform === 'xboxscarlett'
|
||||
|| platform === 'ps5'
|
||||
|| platform === 'freebsd';
|
||||
}
|
||||
|
||||
function koreplatform(platform: string) {
|
||||
if (platform.endsWith('-hl')) return platform.substr(0, platform.length - '-hl'.length);
|
||||
else return platform;
|
||||
}
|
||||
|
||||
let kore = false;
|
||||
let korehl = false;
|
||||
|
||||
async function exportKhaProject(options: Options): Promise<string> {
|
||||
log.info('Creating Kha project.');
|
||||
|
||||
let project: Project = null;
|
||||
let foundProjectFile = false;
|
||||
|
||||
// get the khafile.js and load the config code,
|
||||
// then create the project config object, which contains stuff
|
||||
// like project name, assets paths, sources path, library path...
|
||||
if (fs.existsSync(path.join(options.from, options.projectfile))) {
|
||||
try {
|
||||
project = await loadProject(options.from, options.projectfile, options.target);
|
||||
}
|
||||
catch (x) {
|
||||
log.error(x);
|
||||
throw 'Loading the projectfile failed.';
|
||||
}
|
||||
|
||||
foundProjectFile = true;
|
||||
}
|
||||
|
||||
if (!foundProjectFile) {
|
||||
throw 'No khafile found.';
|
||||
}
|
||||
|
||||
let temp = path.join(options.to, 'temp');
|
||||
fs.ensureDirSync(temp);
|
||||
|
||||
let exporter: KhaExporter = null;
|
||||
|
||||
let target = options.target.toLowerCase();
|
||||
let baseTarget = target;
|
||||
let customTarget: Target = null;
|
||||
if (project.customTargets.get(options.target)) {
|
||||
customTarget = project.customTargets.get(options.target);
|
||||
baseTarget = customTarget.baseTarget;
|
||||
}
|
||||
|
||||
switch (baseTarget) {
|
||||
case Platform.Krom:
|
||||
exporter = new KromExporter(options);
|
||||
break;
|
||||
case Platform.Flash:
|
||||
exporter = new FlashExporter(options);
|
||||
break;
|
||||
case Platform.HTML5:
|
||||
exporter = new Html5Exporter(options);
|
||||
break;
|
||||
case Platform.HTML5Worker:
|
||||
exporter = new Html5WorkerExporter(options);
|
||||
break;
|
||||
case Platform.DebugHTML5:
|
||||
exporter = new DebugHtml5Exporter(options);
|
||||
break;
|
||||
case Platform.WPF:
|
||||
exporter = new WpfExporter(options);
|
||||
break;
|
||||
case Platform.Java:
|
||||
exporter = new JavaExporter(options);
|
||||
break;
|
||||
case Platform.PlayStationMobile:
|
||||
exporter = new PlayStationMobileExporter(options);
|
||||
break;
|
||||
case Platform.Node:
|
||||
exporter = new NodeExporter(options);
|
||||
break;
|
||||
case Platform.Empty:
|
||||
exporter = new EmptyExporter(options);
|
||||
break;
|
||||
default:
|
||||
if (baseTarget.endsWith('-hl')) {
|
||||
korehl = true;
|
||||
options.target = koreplatform(baseTarget);
|
||||
if (!checkKorePlatform(options.target)) {
|
||||
log.error(`Unknown platform: ${target} (baseTarget=$${baseTarget})`);
|
||||
return Promise.reject('');
|
||||
}
|
||||
exporter = new KincHLExporter(options);
|
||||
}
|
||||
else {
|
||||
kore = true;
|
||||
options.target = koreplatform(baseTarget);
|
||||
if (!checkKorePlatform(options.target)) {
|
||||
log.error(`Unknown platform: ${target} (baseTarget=$${baseTarget})`);
|
||||
return Promise.reject('');
|
||||
}
|
||||
exporter = new KincExporter(options);
|
||||
}
|
||||
break;
|
||||
}
|
||||
exporter.setSystemDirectory(target);
|
||||
let buildDir = path.join(options.to, exporter.sysdir() + '-build');
|
||||
|
||||
// Create the target build folder
|
||||
// e.g. 'build/pi'
|
||||
fs.ensureDirSync(path.join(options.to, exporter.sysdir()));
|
||||
|
||||
let defaultWindowOptions = {
|
||||
width: 800,
|
||||
height: 600
|
||||
};
|
||||
|
||||
let windowOptions = project.windowOptions ? project.windowOptions : defaultWindowOptions;
|
||||
exporter.setName(project.name);
|
||||
exporter.setWidthAndHeight(
|
||||
'width' in windowOptions ? windowOptions.width : defaultWindowOptions.width,
|
||||
'height' in windowOptions ? windowOptions.height : defaultWindowOptions.height
|
||||
);
|
||||
|
||||
for (let source of project.sources) {
|
||||
exporter.addSourceDirectory(source);
|
||||
}
|
||||
for (let library of project.libraries) {
|
||||
exporter.addLibrary(library);
|
||||
}
|
||||
exporter.parameters = exporter.parameters.concat(project.parameters);
|
||||
project.scriptdir = options.kha;
|
||||
if (baseTarget !== Platform.Java && baseTarget !== Platform.WPF) {
|
||||
project.addShaders('Sources/Shaders/**', {});
|
||||
}
|
||||
|
||||
for (let callback of Callbacks.preAssetConversion) {
|
||||
callback();
|
||||
}
|
||||
|
||||
let assetConverter = new AssetConverter(exporter, options, project.assetMatchers);
|
||||
lastAssetConverter = assetConverter;
|
||||
let assets = await assetConverter.run(options.watch, temp);
|
||||
|
||||
if ((target === Platform.DebugHTML5 && process.platform === 'win32') || target === Platform.HTML5) {
|
||||
Icon.exportIco(project.icon, path.join(options.to, exporter.sysdir(), 'favicon.ico'), options.from, options);
|
||||
}
|
||||
else if (target === Platform.DebugHTML5) {
|
||||
Icon.exportPng(project.icon, path.join(options.to, exporter.sysdir(), 'favicon.png'), 256, 256, 0xffffffff, true, options.from, options);
|
||||
}
|
||||
|
||||
let shaderDir = path.join(options.to, exporter.sysdir() + '-resources');
|
||||
|
||||
for (let callback of Callbacks.preShaderCompilation) {
|
||||
callback();
|
||||
}
|
||||
fs.ensureDirSync(shaderDir);
|
||||
|
||||
let oldResources: any = null;
|
||||
let recompileAllShaders = false;
|
||||
try {
|
||||
oldResources = JSON.parse(fs.readFileSync(path.join(options.to, exporter.sysdir() + '-resources', 'files.json'), 'utf8'));
|
||||
for (let file of oldResources.files) {
|
||||
if (file.type === 'shader') {
|
||||
if (!file.files || file.files.length === 0) {
|
||||
recompileAllShaders = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
}
|
||||
|
||||
let exportedShaders: CompiledShader[] = [];
|
||||
if (!options.noshaders) {
|
||||
if (fs.existsSync(path.join(options.from, 'Backends'))) {
|
||||
let libdirs = fs.readdirSync(path.join(options.from, 'Backends'));
|
||||
for (let ld in libdirs) {
|
||||
let libdir = path.join(options.from, 'Backends', libdirs[ld]);
|
||||
if (fs.statSync(libdir).isDirectory()) {
|
||||
let exe = path.join(libdir, 'krafix', 'krafix-' + options.target + '.exe');
|
||||
if (fs.existsSync(exe)) {
|
||||
options.krafix = exe;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let shaderCompiler = new ShaderCompiler(exporter, baseTarget, options.krafix, shaderDir, temp,
|
||||
buildDir, options, project.shaderMatchers);
|
||||
lastShaderCompiler = shaderCompiler;
|
||||
try {
|
||||
if (baseTarget !== Platform.Java && baseTarget !== Platform.WPF) {
|
||||
exportedShaders = await shaderCompiler.run(options.watch, recompileAllShaders);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
function findShader(name: string) {
|
||||
let fallback: any = { };
|
||||
fallback.files = [];
|
||||
fallback.inputs = [];
|
||||
fallback.outputs = [];
|
||||
fallback.uniforms = [];
|
||||
fallback.types = [];
|
||||
|
||||
try {
|
||||
for (let file of oldResources.files) {
|
||||
if (file.type === 'shader' && file.name === fixName(name)) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
return fallback;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
let files: {name: string, files: string[], file_sizes: number[], type: string, inputs: any[], outputs: any[], uniforms: any[], types: any[]}[] = [];
|
||||
for (let asset of assets) {
|
||||
let file: any = {
|
||||
name: fixName(asset.name),
|
||||
files: asset.files,
|
||||
file_sizes: asset.file_sizes,
|
||||
type: asset.type
|
||||
};
|
||||
if (file.type === 'image') {
|
||||
file.original_width = asset.original_width;
|
||||
file.original_height = asset.original_height;
|
||||
if (asset.readable) file.readable = asset.readable;
|
||||
}
|
||||
files.push(file);
|
||||
}
|
||||
for (let shader of exportedShaders) {
|
||||
if (shader.noembed) continue;
|
||||
let oldShader = findShader(shader.name);
|
||||
files.push({
|
||||
name: fixName(shader.name),
|
||||
files: shader.files === null ? oldShader.files : shader.files,
|
||||
file_sizes: [1],
|
||||
type: 'shader',
|
||||
inputs: shader.inputs === null ? oldShader.inputs : shader.inputs,
|
||||
outputs: shader.outputs === null ? oldShader.outputs : shader.outputs,
|
||||
uniforms: shader.uniforms === null ? oldShader.uniforms : shader.uniforms,
|
||||
types: shader.types === null ? oldShader.types : shader.types
|
||||
});
|
||||
}
|
||||
|
||||
// Sort to prevent files.json from changing between makes when no files have changed.
|
||||
files.sort(function(a: any, b: any) {
|
||||
if (a.name > b.name) return 1;
|
||||
if (a.name < b.name) return -1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
function secondPass() {
|
||||
// First pass is for main project files. Second pass is for shaders.
|
||||
// Will try to look for the folder, e.g. 'build/Shaders'.
|
||||
// if it exists, export files similar to other a
|
||||
let hxslDir = path.join('build', 'Shaders');
|
||||
/** if (fs.existsSync(hxslDir) && fs.readdirSync(hxslDir).length > 0) {
|
||||
addShaders(exporter, platform, project, from, to.resolve(exporter.sysdir() + '-resources'), temp, from.resolve(Paths.get(hxslDir)), krafix);
|
||||
if (foundProjectFile) {
|
||||
fs.outputFileSync(to.resolve(Paths.get(exporter.sysdir() + '-resources', 'files.json')).toString(), JSON.stringify({ files: files }, null, '\t'), { encoding: 'utf8' });
|
||||
log.info('Assets done.');
|
||||
exportProjectFiles(name, from, to, options, exporter, platform, khaDirectory, haxeDirectory, kore, project.libraries, project.targetOptions, callback);
|
||||
}
|
||||
else {
|
||||
exportProjectFiles(name, from, to, options, exporter, platform, khaDirectory, haxeDirectory, kore, project.libraries, project.targetOptions, callback);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
if (foundProjectFile) {
|
||||
fs.outputFileSync(path.join(options.to, exporter.sysdir() + '-resources', 'files.json'), JSON.stringify({ files: files }, null, '\t'));
|
||||
}
|
||||
|
||||
for (let callback of Callbacks.preHaxeCompilation) {
|
||||
callback();
|
||||
}
|
||||
|
||||
return await exportProjectFiles(project.name, path.join(options.to, exporter.sysdir() + '-resources'), options, exporter, kore, korehl, project.icon,
|
||||
project.libraries, project.targetOptions, project.defines, project.cdefines, project.cflags, project.cppflags, project.stackSize, project.version, project.id);
|
||||
}
|
||||
|
||||
function isKhaProject(directory: string, projectfile: string) {
|
||||
return fs.existsSync(path.join(directory, 'Kha')) || fs.existsSync(path.join(directory, projectfile));
|
||||
}
|
||||
|
||||
async function exportProject(options: Options): Promise<string> {
|
||||
if (isKhaProject(options.from, options.projectfile)) {
|
||||
return await exportKhaProject(options);
|
||||
}
|
||||
else {
|
||||
log.error('Neither Kha directory nor project file (' + options.projectfile + ') found.');
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
function runProject(options: any, name: string): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
log.info('Running...');
|
||||
let run = child_process.spawn(
|
||||
path.join(process.cwd(), options.to, 'linux-build', name),
|
||||
[],
|
||||
{ cwd: path.join(process.cwd(), options.to, 'linux') });
|
||||
|
||||
run.stdout.on('data', function (data: any) {
|
||||
log.info(data.toString());
|
||||
});
|
||||
|
||||
run.stderr.on('data', function (data: any) {
|
||||
log.error(data.toString());
|
||||
});
|
||||
|
||||
run.on('close', function (code: number) {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export let api = 2;
|
||||
|
||||
function findKhaVersion(dir: string): string {
|
||||
let p = path.join(dir, '.git');
|
||||
let hasGitInfo = false;
|
||||
|
||||
if (fs.existsSync(p)) {
|
||||
let stat = fs.statSync(p);
|
||||
hasGitInfo = stat.isDirectory();
|
||||
|
||||
// otherwise git might not utilize an in-place directory
|
||||
if (!hasGitInfo) {
|
||||
let contents = fs.readFileSync(p).toString('utf8', 0, 7);
|
||||
hasGitInfo = contents === 'gitdir:';
|
||||
}
|
||||
}
|
||||
|
||||
if (hasGitInfo) {
|
||||
let gitVersion = 'git-error';
|
||||
try {
|
||||
const output = child_process.spawnSync('git', ['rev-parse', 'HEAD'], {encoding: 'utf8', cwd: dir}).output;
|
||||
for (const str of output) {
|
||||
if (str != null && str.length > 0) {
|
||||
gitVersion = str.substr(0, 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
}
|
||||
|
||||
let gitStatus = 'git-error';
|
||||
try {
|
||||
const output = child_process.spawnSync('git', ['status', '--porcelain'], {encoding: 'utf8', cwd: dir}).output;
|
||||
gitStatus = '';
|
||||
for (const str of output) {
|
||||
if (str != null && str.length > 0) {
|
||||
gitStatus = str.trim();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
}
|
||||
|
||||
if (gitStatus) {
|
||||
return gitVersion + ', ' + gitStatus.replace(/\n/g, ',');
|
||||
}
|
||||
else {
|
||||
return gitVersion;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return '¯\\_(ツ)_/¯';
|
||||
}
|
||||
}
|
||||
|
||||
export async function run(options: Options, loglog: any): Promise<string> {
|
||||
if (options.silent) {
|
||||
log.silent();
|
||||
}
|
||||
else {
|
||||
log.set(loglog);
|
||||
}
|
||||
|
||||
if (options.quiet) {
|
||||
log.silent(true);
|
||||
}
|
||||
|
||||
if (!options.kha) {
|
||||
let p = path.join(__dirname, '..', '..', '..');
|
||||
if (fs.existsSync(p) && fs.statSync(p).isDirectory()) {
|
||||
options.kha = p;
|
||||
}
|
||||
}
|
||||
else {
|
||||
options.kha = path.resolve(options.kha);
|
||||
}
|
||||
log.info('Using Kha (' + findKhaVersion(options.kha) + ') from ' + options.kha);
|
||||
|
||||
if (options.parallelAssetConversion === undefined) {
|
||||
options.parallelAssetConversion = 0;
|
||||
}
|
||||
|
||||
if (!options.haxe) {
|
||||
let haxepath = path.join(options.kha, 'Tools', sysdir());
|
||||
if (fs.existsSync(haxepath) && fs.statSync(haxepath).isDirectory()) options.haxe = haxepath;
|
||||
}
|
||||
|
||||
if (!options.krafix) {
|
||||
let krafixpath = path.join(options.kha, 'Kinc', 'Tools', sysdir(), 'krafix' + sys());
|
||||
if (fs.existsSync(krafixpath)) options.krafix = krafixpath;
|
||||
}
|
||||
|
||||
if (!options.kraffiti) {
|
||||
const kraffitipath = path.join(options.kha, 'Kinc', 'Tools', sysdir(), 'kraffiti' + sys());
|
||||
if (fs.existsSync(kraffitipath)) options.kraffiti = kraffitipath;
|
||||
}
|
||||
else {
|
||||
log.info('Using kraffiti from ' + options.kraffiti);
|
||||
}
|
||||
|
||||
if (!options.ogg && options.ffmpeg) {
|
||||
options.ogg = options.ffmpeg + ' -nostdin -i {in} {out} -y';
|
||||
}
|
||||
|
||||
if (!options.mp3 && options.ffmpeg) {
|
||||
options.mp3 = options.ffmpeg + ' -nostdin -i {in} {out}';
|
||||
}
|
||||
|
||||
if (!options.ogg) {
|
||||
let oggpath = path.join(options.kha, 'Tools', sysdir(), 'oggenc' + sys());
|
||||
if (fs.existsSync(oggpath)) options.ogg = oggpath + ' {in} -o {out} --quiet';
|
||||
}
|
||||
|
||||
if (!options.mp3) {
|
||||
let lamepath = path.join(options.kha, 'Tools', sysdir(), 'lame' + sys());
|
||||
if (fs.existsSync(lamepath)) options.mp3 = lamepath + ' {in} {out}';
|
||||
}
|
||||
|
||||
// if (!options.kravur) {
|
||||
// let kravurpath = path.join(options.kha, 'Tools', 'kravur', 'kravur' + sys());
|
||||
// if (fs.existsSync(kravurpath)) options.kravur = kravurpath + ' {in} {size} {out}';
|
||||
// }
|
||||
|
||||
if (!options.aac && options.ffmpeg) {
|
||||
options.aac = options.ffmpeg + ' -nostdin -i {in} {out}';
|
||||
}
|
||||
|
||||
if (!options.h264 && options.ffmpeg) {
|
||||
options.h264 = options.ffmpeg + ' -nostdin -i {in} {out}';
|
||||
}
|
||||
|
||||
if (!options.webm && options.ffmpeg) {
|
||||
options.webm = options.ffmpeg + ' -nostdin -i {in} {out}';
|
||||
}
|
||||
|
||||
if (!options.wmv && options.ffmpeg) {
|
||||
options.wmv = options.ffmpeg + ' -nostdin -i {in} {out}';
|
||||
}
|
||||
|
||||
if (!options.theora && options.ffmpeg) {
|
||||
options.theora = options.ffmpeg + ' -nostdin -i {in} {out}';
|
||||
}
|
||||
|
||||
if (options.target === 'emscripten') {
|
||||
console.log();
|
||||
console.log('Please note that the html5 target\n'
|
||||
+ 'is usually a better choice.\n'
|
||||
+ 'In particular the html5 target usually runs faster\n'
|
||||
+ 'than the emscripten target. That is because\n'
|
||||
+ 'Haxe and JavaScript are similar in many ways and\n'
|
||||
+ 'therefore the html5 target can make direct use of\n'
|
||||
+ 'all of the optimizations in modern JavaScript\n'
|
||||
+ 'runtimes. The emscripten target on the other hand\n'
|
||||
+ 'has to provide its own garbage collector and many\n'
|
||||
+ 'other performance critical pieces of infrastructure.'
|
||||
);
|
||||
console.log();
|
||||
}
|
||||
|
||||
let name = '';
|
||||
try {
|
||||
name = await exportProject(options);
|
||||
}
|
||||
catch (err) {
|
||||
for (let callback of Callbacks.onFailure) {
|
||||
callback(err);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
for (let callback of Callbacks.postBuild) {
|
||||
callback();
|
||||
}
|
||||
|
||||
if ((options.target === Platform.Linux || options.target === Platform.FreeBSD) && options.run) {
|
||||
await runProject(options, name);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
export function close() {
|
||||
if (lastAssetConverter) lastAssetConverter.close();
|
||||
if (lastShaderCompiler) lastShaderCompiler.close();
|
||||
if (lastHaxeCompiler) lastHaxeCompiler.close();
|
||||
}
|
Reference in New Issue
Block a user