Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,7 @@
export let Architecture = {
Default: 'default',
arm: 'arm',
arm64: 'arm64',
x86: 'x86',
x64: 'x64'
};

279
kmake/src/AssetConverter.ts Normal file
View File

@ -0,0 +1,279 @@
import * as fs from 'kmake/fsextra';
import * as path from 'path';
import * as log from 'kmake/log';
import * as crypto from 'crypto';
import { Exporter } from 'kmake/Exporters/Exporter';
export class AssetConverter {
exporter: Exporter;
platform: string;
assetMatchers: Array<{ match: string, options: any }>;
watcher: fs.FSWatcher;
constructor(exporter: Exporter, platform: string, assetMatchers: Array<{ match: string, options: any }>) {
this.exporter = exporter;
this.platform = platform;
this.assetMatchers = assetMatchers;
}
close(): void {
if (this.watcher) this.watcher.close();
}
static replacePattern(pattern: string, value: 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, value).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 = fs.watch(match); // , { ignored: /[\/\\]\.(svn|git|DS_Store)/, persistent: watch, followSymlinks: false });
const onFileChange = async (file: string) => {
const fileinfo = path.parse(file);
let outPath = fileinfo.dir + path.sep + fileinfo.name;
// with subfolders
if (options.destination != null) {
outPath = path.relative(options.baseDir, outPath);
}
else { // flat
outPath = fileinfo.name;
}
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, 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, 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, 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, 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, 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
kmake/src/AudioApi.ts Normal file
View File

@ -0,0 +1,5 @@
export let AudioApi = {
Default: 'default',
DirectSound: 'directsound',
WASAPI: 'wasapi'
};

41
kmake/src/Block.ts Normal file
View File

@ -0,0 +1,41 @@
import * as fs from 'fs';
export class Block {
out: number;
indentation: number;
constructor(out: number, indentation: number) {
this.out = out;
this.indentation = indentation;
}
indent() {
++this.indentation;
}
unindent() {
--this.indentation;
}
tag(name: string, value: string) {
this.p('<' + name + '>' + value + '</' + name + '>');
}
tagStart(name: string) {
this.p('<' + name + '>');
this.indent();
}
tagEnd(name: string) {
this.unindent();
this.p('</' + name + '>');
}
p(line: string) {
if (line === undefined) line = '';
let tabs = '';
for (let i = 0; i < this.indentation; ++i) tabs += '\t';
let data = Buffer.from(tabs + line + '\n');
fs.writeSync(this.out, data, 0, data.length, null);
}
}

95
kmake/src/ClCompile.ts Normal file
View File

@ -0,0 +1,95 @@
import { Block } from './Block';
import { Platform } from './Platform';
function toLine(options: any) {
let line = '';
for (let option of options) {
line += option + ';';
}
return line;
}
export class ClCompile extends Block {
platform: string;
includes: string[];
defines: string[];
minimalRebuild: boolean;
userPreprocessorDefinitions: string;
functionLevelLinking: boolean;
stringPooling: boolean;
prefast: boolean;
favorSize: boolean;
fastCap: boolean;
warningLevel: number;
optimization: boolean;
runtimeLibrary: string;
multiProcessorCompilation: boolean;
objectFileName: string;
generateDebugInformation: boolean;
constructor(out: number, indentation: number, platform: string, configuration: string, includes: string[], defines: string[]) {
super(out, indentation);
this.platform = platform;
this.includes = includes;
this.defines = defines;
this.minimalRebuild = false;
// PS3
this.userPreprocessorDefinitions = '';
// Xbox360
this.functionLevelLinking = false;
this.stringPooling = false;
this.prefast = false;
this.favorSize = false;
this.fastCap = false;
this.warningLevel = 3;
this.optimization = false;
this.runtimeLibrary = 'MultiThreadedDebug';
this.multiProcessorCompilation = true;
this.objectFileName = '$(IntDir)\\build\\%(RelativeDir)';
this.generateDebugInformation = false;
// this.configuration = configuration;
switch (platform) {
case Platform.WindowsApp:
this.includes.push('$(ProjectDir)');
this.includes.push('$(IntermediateOutputPath)');
this.includes.push('%(AdditionalIncludeDirectories)');
this.defines.push('_UNICODE');
this.defines.push('UNICODE');
this.defines.push('%(PreprocessorDefinitions)');
break;
default:
break;
}
}
print() {
let defineLine = toLine(this.defines);
let includeLine = toLine(this.includes);
this.tagStart('ClCompile');
this.tag('AdditionalIncludeDirectories', includeLine);
if (this.platform === Platform.Windows) {
this.tag('WarningLevel', 'Level' + this.warningLevel);
this.tag('Optimization', this.optimization ? 'Enabled' : 'Disabled');
this.tag('PreprocessorDefinitions', defineLine);
this.tag('RuntimeLibrary', this.runtimeLibrary);
this.tag('MultiProcessorCompilation', this.multiProcessorCompilation ? 'true' : 'false');
this.tag('MinimalRebuild', this.minimalRebuild ? 'true' : 'false');
this.tag('ObjectFileName', this.objectFileName);
this.tag('SDLCheck', 'true');
}
else if (this.platform === Platform.WindowsApp) {
// tag("PreprocessorDefinitions", defineLine);
this.tag('PrecompiledHeader', 'NotUsing');
this.tag('ObjectFileName', this.objectFileName);
this.tag('DisableSpecificWarnings', '4453');
this.tag('SDLCheck', 'true');
}
this.tagEnd('ClCompile');
}
}

8
kmake/src/Compiler.ts Normal file
View File

@ -0,0 +1,8 @@
export enum Compiler {
Default = 'default',
GCC = 'gcc',
Clang = 'clang',
MSVC = 'msvc',
MuslGcc = 'musl-gcc',
Custom = 'custom'
}

View File

@ -0,0 +1,8 @@
export let Configuration = {
Debug: 'Debug',
CodeAnalysis: 'CodeAnalysis',
Profile: 'Profile',
Profile_FastCap: 'Profile_FastCap',
Release: 'Release',
Release_LTCG: 'Release_LTCG'
};

View File

@ -0,0 +1,315 @@
import { Exporter } from 'kmake/Exporters/Exporter';
import { GraphicsApi } from 'kmake/GraphicsApi';
import { Project } from 'kmake/Project';
import { Architecture } from 'kmake/Architecture';
import { Options } from 'kmake/Options';
import * as Icon from 'kmake/Icon';
import * as fs from 'kmake/fsextra';
import * as os from 'os';
import * as path from 'path';
import { CompilerCommandsExporter } from 'kmake/Exporters/CompileCommandsExporter';
interface TargetOptions {
package: string;
installLocation: string;
versionCode: number;
versionName: string;
compileSdkVersion: number;
minSdkVersion: number;
targetSdkVersion: number;
screenOrientation: string;
permissions: string[];
disableStickyImmersiveMode: boolean;
metadata: string[];
customFilesPath?: string;
buildGradlePath: string;
globalBuildGradlePath: string;
proguardRulesPath: string;
abiFilters: string[];
}
export class AndroidExporter extends Exporter {
compileCommands: CompilerCommandsExporter;
safeName: string;
constructor(options: any) {
super(options);
this.compileCommands = new CompilerCommandsExporter(options);
}
async exportSolution(project: Project, from: string, to: string, platform: string, vrApi: any, options: any) {
this.safeName = project.getSafeName();
const indir = path.join(__dirname, '..', '..', 'Data', 'android');
const outdir = path.join(to.toString(), this.safeName);
fs.ensureDirSync(outdir);
const targetOptions: TargetOptions = {
package: 'tech.kore',
installLocation: 'internalOnly',
versionCode: 1,
versionName: '1.0',
compileSdkVersion: 33,
minSdkVersion: (Options.graphicsApi === GraphicsApi.Vulkan || Options.graphicsApi === GraphicsApi.Default) ? 24 : 21,
targetSdkVersion: 33,
screenOrientation: 'sensor',
permissions: ['android.permission.VIBRATE'],
disableStickyImmersiveMode: false,
metadata: new Array<string>(),
customFilesPath: null,
buildGradlePath: null,
globalBuildGradlePath: null,
proguardRulesPath: null,
abiFilters: new Array<string>()
};
if (project.targetOptions != null && project.targetOptions.android != null) {
const userOptions = project.targetOptions.android;
for (let key in userOptions) {
if (userOptions[key] == null) continue;
switch (key) {
case 'customFilesPath':
case 'buildGradlePath':
case 'globalBuildGradlePath':
case 'proguardRulesPath':
// fix path slashes and normalize
const p: string = userOptions[key].split('/').join(path.sep);
(targetOptions as any)[key] = path.join(from, p);
break;
default:
(targetOptions as any)[key] = userOptions[key];
}
}
}
const binaryData = require('fs').getEmbeddedBinaryData();
const textData = require('fs').getEmbeddedData();
fs.writeFileSync(path.join(outdir, '.gitignore'), textData['android_gitignore']);
if (targetOptions.globalBuildGradlePath) {
fs.copyFileSync(targetOptions.globalBuildGradlePath, path.join(outdir, 'build.gradle.kts'));
}
else {
fs.writeFileSync(path.join(outdir, 'build.gradle.kts'), textData['android_build_gradle']);
}
fs.writeFileSync(path.join(outdir, 'gradle.properties'), textData['android_gradle_properties']);
fs.writeFileSync(path.join(outdir, 'gradlew'), textData['android_gradlew']);
if (os.platform() !== 'win32') {
fs.chmodSync(path.join(outdir, 'gradlew'), 0o755);
}
fs.writeFileSync(path.join(outdir, 'gradlew.bat'), textData['android_gradlew_bat']);
let settings = textData['android_settings_gradle'];
settings = settings.replace(/{name}/g, project.getName());
fs.writeFileSync(path.join(outdir, 'settings.gradle.kts'), settings);
fs.ensureDirSync(path.join(outdir, 'app'));
fs.writeFileSync(path.join(outdir, 'app', '.gitignore'), textData['android_app_gitignore']);
if (targetOptions.proguardRulesPath) {
fs.copyFileSync(targetOptions.proguardRulesPath, path.join(outdir, 'app', 'proguard-rules.pro'));
}
else {
fs.writeFileSync(path.join(outdir, 'app', 'proguard-rules.pro'), textData['android_app_proguard_rules_pro']);
}
this.writeAppGradle(project, outdir, from, targetOptions, textData);
this.writeCMakeLists(project, outdir, from, targetOptions, textData);
fs.ensureDirSync(path.join(outdir, 'app', 'src'));
// fs.emptyDirSync(path.join(outdir, 'app', 'src'));
fs.ensureDirSync(path.join(outdir, 'app', 'src', 'main'));
this.writeManifest(outdir, targetOptions, textData);
let strings = textData['android_main_res_values_strings_xml'];
strings = strings.replace(/{name}/g, project.getName());
fs.ensureDirSync(path.join(outdir, 'app', 'src', 'main', 'res', 'values'));
fs.writeFileSync(path.join(outdir, 'app', 'src', 'main', 'res', 'values', 'strings.xml'), strings);
await this.exportIcons(project.icon, outdir, from, to);
fs.ensureDirSync(path.join(outdir, 'gradle', 'wrapper'));
fs.writeFileSync(path.join(outdir, 'gradle', 'wrapper', 'gradle-wrapper.jar'), binaryData['android_gradle_wrapper_gradle_wrapper_jar']);
fs.writeFileSync(path.join(outdir, 'gradle', 'wrapper', 'gradle-wrapper.properties'), textData['android_gradle_wrapper_gradle_wrapper_properties']);
/*fs.ensureDirSync(path.join(outdir, '.idea'));
fs.writeFileSync(path.join(outdir, '.idea', '.gitignore'), textData['android_idea_gitignore']);
fs.writeFileSync(path.join(outdir, '.idea', 'gradle.xml'), textData['android_idea_gradle_xml']);
fs.writeFileSync(path.join(outdir, '.idea', 'misc.xml'), textData['android_idea_misc_xml']);
let modules = textData['android_idea_modules_xml'];
modules = modules.replace(/{name}/g, project.getName());
fs.writeFileSync(path.join(outdir, '.idea', 'modules.xml'), modules);
fs.writeFileSync(path.join(outdir, '.idea', 'compiler.xml'), textData['android_idea_compiler_xml']);
fs.writeFileSync(path.join(outdir, '.idea', 'kotlinc.xml'), textData['android_idea_kotlinc_xml']);
fs.ensureDirSync(path.join(outdir, '.idea', 'modules'));
fs.ensureDirSync(path.join(outdir, '.idea', 'modules', 'app'));
fs.writeFileSync(path.join(outdir, '.idea', 'modules', 'app', project.getName() + '.app.main.iml'), textData['android_idea_modules_my_application_iml']);*/
if (targetOptions.customFilesPath != null) {
const dir = targetOptions.customFilesPath;
if (!fs.existsSync(dir)) throw dir + ' folder does not exist';
fs.copyDirSync(dir, outdir);
}
if (project.getDebugDir().length > 0) fs.copyDirSync(path.resolve(from, project.getDebugDir()), path.resolve(to, this.safeName, 'app', 'src', 'main', 'assets'));
this.compileCommands.exportSolution(project, from, to, platform, vrApi, options);
}
writeAppGradle(project: Project, outdir: string, from: string, targetOptions: TargetOptions, textData: any) {
let cflags = '';
for (let flag of project.cFlags)
cflags += flag + ' ';
let cppflags = '';
for (let flag of project.cppFlags)
cppflags += flag + ' ';
let gradle = null;
if (targetOptions.buildGradlePath) {
gradle = fs.readFileSync(targetOptions.buildGradlePath, 'utf8');
}
else {
gradle = textData['android_app_build_gradle'];
}
gradle = gradle.replace(/{package}/g, targetOptions.package);
gradle = gradle.replace(/{versionCode}/g, targetOptions.versionCode.toString());
gradle = gradle.replace(/{versionName}/g, targetOptions.versionName);
gradle = gradle.replace(/{compileSdkVersion}/g, targetOptions.compileSdkVersion.toString());
gradle = gradle.replace(/{minSdkVersion}/g, targetOptions.minSdkVersion.toString());
gradle = gradle.replace(/{targetSdkVersion}/g, targetOptions.targetSdkVersion.toString());
let arch = '';
if (targetOptions.abiFilters.length > 0) {
for (let item of targetOptions.abiFilters) {
if (arch.length === 0) {
arch = '"' + item + '"';
}
else {
arch = arch + ', "' + item + '"';
}
}
arch = `ndk { abiFilters += listOf(${arch}) }`;
}
else {
switch (Options.architecture) {
case Architecture.Default: arch = ''; break;
case Architecture.arm: arch = 'armeabi-v7a'; break;
case Architecture.arm64: arch = 'arm64-v8a'; break;
case Architecture.x64: arch = 'x86'; break;
case Architecture.x64: arch = 'x86_64'; break;
default: throw 'Unknown architecture ' + Options.architecture;
}
if (Options.architecture !== Architecture.Default) {
arch = `ndk {abiFilters += listOf("${arch}")}`;
}
}
gradle = gradle.replace(/{architecture}/g, arch);
gradle = gradle.replace(/{cflags}/g, cflags);
cppflags = '-frtti -fexceptions ' + cppflags;
if (project.cppStd !== '') {
cppflags = '-std=' + project.cppStd + ' ' + cppflags;
}
gradle = gradle.replace(/{cppflags}/g, cppflags);
let javasources = '';
for (let dir of project.getJavaDirs()) {
javasources += '"' + path.relative(path.join(outdir, 'app'), path.resolve(from, dir)).replace(/\\/g, '/') + '", ';
}
javasources += '"' + path.relative(path.join(outdir, 'app'), path.join(Project.koreDir.toString(), 'Backends', 'System', 'Android', 'Java-Sources')).replace(/\\/g, '/') + '"';
gradle = gradle.replace(/{javasources}/g, javasources);
fs.writeFileSync(path.join(outdir, 'app', 'build.gradle.kts'), gradle);
}
writeCMakeLists(project: Project, outdir: string, from: string, targetOptions: TargetOptions, textData: any) {
let cmake = textData['android_app_cmakelists_txt'];
let debugDefines = '';
for (const def of project.getDefines()) {
if (!def.config || def.config.toLowerCase() === 'debug') {
debugDefines += ' -D' + def.value.replace(/\"/g, '\\\\\\\"');
}
}
cmake = cmake.replace(/{debug_defines}/g, debugDefines);
let releaseDefines = '';
for (const def of project.getDefines()) {
if (!def.config || def.config.toLowerCase() === 'release') {
releaseDefines += ' -D' + def.value.replace(/\"/g, '\\\\\\\"');
}
}
cmake = cmake.replace(/{release_defines}/g, releaseDefines);
let includes = '';
for (let inc of project.getIncludeDirs()) {
includes += ' "' + path.resolve(inc).replace(/\\/g, '/') + '"\n';
}
cmake = cmake.replace(/{includes}/g, includes);
let files = '';
for (let file of project.getFiles()) {
if (file.file.endsWith('.c') || file.file.endsWith('.cc')
|| file.file.endsWith('.cpp') || file.file.endsWith('.h')) {
if (file.options && file.options.nocompile) {
continue;
}
if (path.isAbsolute(file.file)) {
files += ' "' + path.resolve(file.file).replace(/\\/g, '/') + '"\n';
}
else {
files += ' "' + path.resolve(path.join(from, file.file)).replace(/\\/g, '/') + '"\n';
}
}
}
cmake = cmake.replace(/{files}/g, files);
let libraries1 = '';
let libraries2 = '';
for (let lib of project.getLibs()) {
libraries1 += 'find_library(' + lib + '-lib ' + lib + ')\n';
libraries2 += ' ${' + lib + '-lib}\n';
}
cmake = cmake.replace(/{libraries1}/g, libraries1)
.replace(/{libraries2}/g, libraries2);
const cmakePath = path.join(outdir, 'app', 'CMakeLists.txt');
if (this.isCmakeSame(cmakePath, cmake)) return;
fs.writeFileSync(cmakePath, cmake);
}
isCmakeSame(cmakePath: string, cmake: string): boolean {
// prevent overwriting CMakeLists.txt if it has not changed
if (!fs.existsSync(cmakePath)) return false;
return fs.readFileSync(cmakePath, 'utf8') === cmake;
}
writeManifest(outdir: string, targetOptions: TargetOptions, textData: any) {
let manifest = textData['android_main_androidmanifest_xml'];
manifest = manifest.replace(/{package}/g, targetOptions.package);
manifest = manifest.replace(/{installLocation}/g, targetOptions.installLocation);
manifest = manifest.replace(/{versionCode}/g, targetOptions.versionCode.toString());
manifest = manifest.replace(/{versionName}/g, targetOptions.versionName);
manifest = manifest.replace(/{screenOrientation}/g, targetOptions.screenOrientation);
manifest = manifest.replace(/{targetSdkVersion}/g, targetOptions.targetSdkVersion);
manifest = manifest.replace(/{permissions}/g, targetOptions.permissions.map((p) => { return '\n\t<uses-permission android:name="' + p + '"/>'; }).join(''));
let metadata = targetOptions.disableStickyImmersiveMode ? '\n\t\t<meta-data android:name="disableStickyImmersiveMode" android:value="true"/>' : '';
for (const meta of targetOptions.metadata) {
metadata += '\n\t\t' + meta;
}
manifest = manifest.replace(/{metadata}/g, metadata);
fs.ensureDirSync(path.join(outdir, 'app', 'src', 'main'));
fs.writeFileSync(path.join(outdir, 'app', 'src', 'main', 'AndroidManifest.xml'), manifest);
}
async exportIcons(icon: string, outdir: string, from: string, to: string) {
const folders = ['mipmap-mdpi', 'mipmap-hdpi', 'mipmap-xhdpi', 'mipmap-xxhdpi', 'mipmap-xxxhdpi'];
const dpis = [48, 72, 96, 144, 192];
for (let i = 0; i < dpis.length; ++i) {
const folder = folders[i];
const dpi = dpis[i];
fs.ensureDirSync(path.join(outdir, 'app', 'src', 'main', 'res', folder));
await Icon.exportPng(icon, path.resolve(to, this.safeName, 'app', 'src', 'main', 'res', folder, 'ic_launcher.png'), dpi, dpi, undefined, from);
await Icon.exportPng(icon, path.resolve(to, this.safeName, 'app', 'src', 'main', 'res', folder, 'ic_launcher_round.png'), dpi, dpi, undefined, from);
}
}
}

View File

@ -0,0 +1,32 @@
import { Project } from 'kmake/Project';
import * as fs from 'kmake/fsextra';
import * as path from 'path';
import { Exporter } from 'kmake/Exporters/Exporter';
import { CMakeExporter } from 'kmake/Exporters/CMakeExporter';
export class CLionExporter extends Exporter {
cmake: CMakeExporter;
constructor(options: any) {
super(options);
this.cmake = new CMakeExporter(options);
}
async exportSolution(project: Project, from: string, to: string, platform: string, vrApi: any, options: any): Promise<void> {
let name = project.getSafeName();
fs.ensureDirSync(path.resolve(to, name, '.idea'));
let misc = require('fs').getEmbeddedData()['linux_idea_misc_xml'];
misc = misc.replace(/{root}/g, path.resolve(from));
fs.writeFileSync(path.join(to, name, '.idea', 'misc.xml'), misc, 'utf8');
let workspace = require('fs').getEmbeddedData()['linux_idea_workspace_xml'];
workspace = workspace.replace(/{workingdir}/g, path.resolve(project.getDebugDir()));
workspace = workspace.replace(/{project}/g, name);
workspace = workspace.replace(/{target}/g, name);
fs.writeFileSync(path.join(to, name, '.idea', 'workspace.xml'), workspace, 'utf8');
this.cmake.exportSolution(project, from, to, platform, vrApi, options);
}
}

View File

@ -0,0 +1,180 @@
import { Project } from 'kmake/Project';
import * as path from 'path';
import { Exporter } from 'kmake/Exporters/Exporter';
export class CMakeExporter extends Exporter {
constructor(options: any) {
super(options);
}
async exportSolution(project: Project, from: string, to: string, platform: string, vrApi: any, options: any): Promise<void> {
let name = project.getSafeName();
this.writeFile(path.resolve(to, name, 'CMakeLists.txt'));
let cmakeVersion = '3.10';
switch (project.cppStd) {
case 'gnu++03':
case 'c++03':
case 'gnu++11':
case 'c++11':
case 'gnu++14':
case 'c++14':
case 'gnu++17':
case 'c++17':
break;
case 'gnu++2a':
case 'c++2a':
case 'gnu++20':
case 'c++20':
cmakeVersion = '3.12';
break;
case 'gnu++2b':
case 'c++2b':
case 'gnu++23':
case 'c++23':
cmakeVersion = '3.20';
break;
default:
break;
}
switch (project.cStd) {
case 'gnu9x':
case 'gnu99':
case 'c9x':
case 'c99':
case 'gnu1x':
case 'gnu11':
case 'c1x':
case 'c11':
break;
case 'gnu18':
case 'gnu17':
case 'c18':
case 'c17':
case 'gnu2x':
case 'c2x':
cmakeVersion = '3.21';
break;
default:
break;
}
this.p('cmake_minimum_required(VERSION ' + cmakeVersion + ')');
this.p('project(' + name + ')');
switch (project.cppStd) {
case 'gnu++03':
case 'c++03':
this.p('set(CMAKE_CXX_STANDARD 03)');
break;
case 'gnu++11':
case 'c++11':
this.p('set(CMAKE_CXX_STANDARD 11)');
break;
case 'gnu++14':
case 'c++14':
this.p('set(CMAKE_CXX_STANDARD 14)');
break;
case 'gnu++17':
case 'c++17':
this.p('set(CMAKE_CXX_STANDARD 17)');
break;
case 'gnu++2a':
case 'c++2a':
case 'gnu++20':
case 'c++20':
this.p('set(CMAKE_CXX_STANDARD 20)');
break;
case 'gnu++2b':
case 'c++2b':
case 'gnu++23':
case 'c++23':
this.p('set(CMAKE_CXX_STANDARD 23)');
break;
default:
this.p('set(CMAKE_CXX_STANDARD 98)');
break;
}
switch (project.cStd) {
case 'gnu9x':
case 'gnu99':
case 'c9x':
case 'c99':
this.p('set(CMAKE_C_STANDARD 99)');
break;
case 'gnu1x':
case 'gnu11':
case 'c1x':
case 'c11':
this.p('set(CMAKE_C_STANDARD 11)');
break;
case 'gnu18':
case 'gnu17':
case 'c18':
case 'c17':
this.p('set(CMAKE_C_STANDARD 17)');
break;
case 'gnu2x':
case 'c2x':
this.p('set(CMAKE_C_STANDARD 23)');
break;
default:
this.p('set(CMAKE_C_STANDARD 99)');
break;
}
this.p('set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -static-libgcc -static-libstdc++")');
let debugDefines = '';
for (const def of project.getDefines()) {
if (!def.config || def.config.toLowerCase() === 'debug') {
debugDefines += ' -D' + def.value;
}
}
this.p('set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}' + debugDefines + '")');
this.p('set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}' + debugDefines + '")');
let releaseDefines = '';
for (const def of project.getDefines()) {
if (!def.config || def.config.toLowerCase() === 'release') {
releaseDefines += ' -D' + def.value;
}
}
releaseDefines += ' -DNDEBUG';
this.p('set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}' + releaseDefines + '")');
this.p('set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}' + releaseDefines + '")');
let includes = '';
for (let inc of project.getIncludeDirs()) {
includes += ' "' + path.resolve(inc).replace(/\\/g, '/') + '"\n';
}
this.p('include_directories(\n' + includes + ')');
let files = '';
for (let file of project.getFiles()) {
if (file.file.endsWith('.c') || file.file.endsWith('.cc') || file.file.endsWith('.cpp') || file.file.endsWith('.h')) {
if (file.options && file.options.nocompile) {
continue;
}
files += ' "' + path.resolve(file.file).replace(/\\/g, '/') + '"\n';
}
}
this.p('set(SOURCE_FILES\n' + files + ')');
this.p('add_executable(' + name + ' ${SOURCE_FILES})');
let libraries = '';
for (let lib of project.getLibs()) {
libraries += ' ' + lib + '\n';
}
this.p('target_link_libraries(' + name + '\n' + libraries + ')');
this.closeFile();
}
}

View File

@ -0,0 +1,158 @@
import { Project } from 'kmake/Project';
import * as fs from 'kmake/fsextra';
import * as path from 'path';
import { Platform } from 'kmake/Platform';
import * as log from 'kmake/log';
import * as os from 'os';
import * as child_process from 'child_process';
import { Exporter } from 'kmake/Exporters/Exporter';
export class CompilerCommandsExporter extends Exporter {
constructor(options: any) {
super(options);
}
async exportSolution(project: Project, _from: string, to: string, platform: string, vrApi: any, options: any): Promise<void> {
let from = path.resolve(process.cwd(), _from);
this.writeFile(path.resolve(to, 'compile_commands.json'));
let includes = [];
for (let inc of project.getIncludeDirs()) {
includes.push('-I');
includes.push(path.resolve(from, inc));
}
let defines = [];
for (let def of project.getDefines()) {
defines.push('-D');
defines.push(def.value.replace(/\"/g, '\\"'));
}
let objects: any = {};
let ofiles: any = {};
for (let fileobject of project.getFiles()) {
let file = fileobject.file;
if (file.endsWith('.cpp') || file.endsWith('.c') || file.endsWith('.cc') || file.endsWith('.s') || file.endsWith('.S')) {
if (fileobject.options && fileobject.options.nocompile) {
continue;
}
let name = file.toLowerCase();
if (name.indexOf('/') >= 0) name = name.substr(name.lastIndexOf('/') + 1);
name = name.substr(0, name.lastIndexOf('.'));
if (!objects[name]) {
objects[name] = true;
ofiles[file] = name;
}
else {
while (objects[name]) {
name = name + '_';
}
objects[name] = true;
ofiles[file] = name;
}
}
}
let defaultArgs = [];
// TODO: same for MacOS, Windows, etc...
if (platform === Platform.Android) {
defaultArgs.push('--target=aarch64-none-linux-android21');
defaultArgs.push('-DANDROID');
// take a guess at where the ndk could be
function ndkFromSdkRoot() {
let sdkEnv = process.env['ANDROID_HOME'] ?? process.env['ANDROID_SDK_ROOT'];
if (!sdkEnv) return null;
let ndk_dir = path.join(sdkEnv, 'ndk');
if (!fs.existsSync(ndk_dir)) {
return null;
}
let ndks = fs.readdirSync(ndk_dir);
ndks = ndks.filter(item => !item.startsWith("."));
if (ndks.length < 1) {
return null;
}
return path.join(ndk_dir, ndks[0]);
}
let android_ndk = process.env['ANDROID_NDK'] ?? ndkFromSdkRoot();
if (android_ndk) {
let host_tag = '';
switch (os.platform()) {
// known host tags
// TODO: figure out the host tag for aarch64 darwin/linux/windows
case 'linux':
host_tag = 'linux-x86_64';
break;
case 'darwin':
host_tag = 'darwin-x86_64';
break;
case 'win32':
host_tag = 'windows-x86_64';
break;
}
let ndk_toolchain = path.join(android_ndk, `toolchains/llvm/prebuilt/${host_tag}`);
if (host_tag !== '' && fs.existsSync(ndk_toolchain)) {
defaultArgs.push(`--gcc-toolchain=${ndk_toolchain}`);
defaultArgs.push(`--sysroot=${ndk_toolchain}/sysroot`);
}
else {
// fallback to the first found toolchain
let toolchains = fs.readdirSync(path.join(android_ndk, `toolchains/llvm/prebuilt/`));
if (toolchains.length > 0) {
let host_tag = toolchains[0];
let ndk_toolchain = path.join(android_ndk, `toolchains/llvm/prebuilt/${host_tag}`);
defaultArgs.push(`--gcc-toolchain=${ndk_toolchain}`);
defaultArgs.push(`--sysroot=${ndk_toolchain}/sysroot`);
log.info(`Found android ndk toolchain in ${ndk_toolchain}.`);
}
else {
log.error('Platform is set to Android, but android toolchain not found.');
}
}
}
else {
log.error('Platform is set to Android, but android toolchain not found.\nPlease set the ANDROID_NDK environment variable if you need a compile_commands.json for your IDE.');
}
}
else if (platform === Platform.Emscripten) {
const emcc_bin = (process.platform === 'win32') ? 'emcc.bat' : 'emcc';
let emcc = child_process.spawnSync(emcc_bin, ['--cflags']);
// log.info(emcc.status);
if (emcc.status === 0) {
let flags = emcc.output.toString().split(' ');
defaultArgs.push(...flags);
}
else {
log.error('Platform is set to Emscripten, but emcc could not be found. Please add it to your PATH environment variable if you need a compile_commands.json for your IDE.');
}
}
let commands = [];
for (let fileobject of project.getFiles()) {
let file = fileobject.file;
if (file.endsWith('.c') || file.endsWith('.cpp') || file.endsWith('.cc')) {
let args = ['/usr/bin/clang', '-c', '-o', (options.debug ? 'debug' : 'release') + ofiles[file] + '.o'];
if (file.endsWith('.c')) {
args.push('-std=c99');
}
args.push(...defaultArgs);
args.push(path.resolve(from, file));
let command = {
directory: from,
file: path.resolve(from, file),
output: path.resolve(to, ofiles[file] + '.o'),
arguments: args.concat(includes).concat(defines)
};
commands.push(command);
}
}
this.p(JSON.stringify(commands));
this.closeFile();
}
}

View File

@ -0,0 +1,59 @@
import { Exporter } from 'kmake/Exporters/Exporter';
import { Project } from 'kmake/Project';
import { Options } from 'kmake/Options';
import { GraphicsApi } from 'kmake/GraphicsApi';
import * as fs from 'kmake/fsextra';
import * as path from 'path';
import { CompilerCommandsExporter } from 'kmake/Exporters/CompileCommandsExporter';
import { MakeExporter } from 'kmake/Exporters/MakeExporter';
import { NinjaExporter } from 'kmake/Exporters/NinjaExporter';
import * as Icon from 'kmake/Icon';
export class EmscriptenExporter extends Exporter {
compileCommands: CompilerCommandsExporter;
make: MakeExporter;
ninja: NinjaExporter;
constructor(project: Project, options: any) {
super(options);
this.compileCommands = new CompilerCommandsExporter(options);
let linkerFlags = '-static-libgcc -static-libstdc++';
if (project.targetOptions.emscripten.threads) {
linkerFlags += ' -pthread';
}
linkerFlags += ' -sTOTAL_MEMORY=134217728 ';
linkerFlags += ' --preload-file ' + this.debugDirName(project);
const emcc = (process.platform === 'win32') ? 'emcc.bat' : 'emcc';
this.make = new MakeExporter(options, emcc, emcc, '', '', linkerFlags, '.html');
this.ninja = new NinjaExporter(options, emcc, emcc, '', '', linkerFlags, '.html');
}
debugDirName(project: Project): string {
let name = project.getDebugDir();
name = name.replace(/\\/g, '/');
if (name.endsWith('/')) {
name = name.substr(0, name.length - 1);
}
if (name.lastIndexOf('/') >= 0) {
name = name.substr(name.lastIndexOf('/') + 1);
}
return name;
}
async exportSolution(project: Project, from: string, to: string, platform: string, vrApi: any, options: any) {
let outputPath = path.resolve(to, options.buildPath);
fs.ensureDirSync(outputPath);
fs.copyDirSync(path.resolve(from, this.debugDirName(project)), path.resolve(outputPath, this.debugDirName(project)));
this.make.exportSolution(project, from, to, platform, vrApi, options);
this.ninja.exportSolution(project, from, to, platform, vrApi, options);
this.compileCommands.exportSolution(project, from, to, platform, vrApi, options);
await Icon.exportIco(project.icon, path.resolve(outputPath, 'favicon.ico'), from, true);
}
}

View File

@ -0,0 +1,79 @@
import { Project } from 'kmake/Project';
import * as fs from 'kmake/fsextra';
import * as path from 'path';
import * as log from 'kmake/log';
enum WriteMode {
None,
File,
Stdout
}
export abstract class Exporter {
writeMode: WriteMode = WriteMode.None;
outFile: number = 0;
outString: string = null;
constructor(options: any) {
}
writeFile(file: string) {
this.writeMode = WriteMode.File;
this.outFile = fs.openSync(file, 'w');
this.outString = null;
}
writeStdout() {
this.writeMode = WriteMode.Stdout;
this.outString = '';
this.outFile = 0;
}
closeFile() {
fs.closeSync(this.outFile);
this.outFile = 0;
this.outString = null;
this.writeMode = WriteMode.None;
}
closeStdout() {
this.outString = null;
this.outFile = 0;
this.writeMode = WriteMode.None;
}
p(line: string = '', indent: number = 0) {
let tabs = '';
for (let i = 0; i < indent; ++i) {
tabs += '\t';
}
if (this.writeMode === WriteMode.Stdout) {
log.info(tabs + line);
}
else if (this.writeMode === WriteMode.File) {
let data = Buffer.from(tabs + line + '\n');
fs.writeSync(this.outFile, data, 0, data.length, null);
}
else {
throw 'Writing while not actually writing';
}
}
nicePath(from: string, to: string, filepath: string): string {
let absolute = filepath;
if (!path.isAbsolute(absolute)) {
absolute = path.resolve(from, filepath);
}
return path.relative(to, absolute);
}
async exportSolution(project: Project, from: string, to: string, platform: string, vrApi: any, options: any): Promise<void> {
return new Promise<void>((resolve, reject) => {
reject('Called an abstract function');
});
}
open(project: Project, to: string) {}
}

View File

@ -0,0 +1,424 @@
import { Exporter } from 'kmake/Exporters/Exporter';
import { Options } from 'kmake/Options';
import { Platform } from 'kmake/Platform';
import { Project } from 'kmake/Project';
import { Compiler } from 'kmake/Compiler';
import * as fs from 'kmake/fsextra';
import * as path from 'path';
import { CLionExporter } from 'kmake/Exporters/CLionExporter';
export class FreeBSDExporter extends Exporter {
clion: CLionExporter;
constructor(options: any) {
super(options);
this.clion = new CLionExporter(options);
}
async exportSolution(project: Project, from: string, to: string, platform: string, vrApi: any, options: any) {
this.exportMakefile(project, from, to, platform, vrApi, options);
this.exportCodeBlocks(project, from, to, platform, vrApi, options);
this.clion.exportSolution(project, from, to, platform, vrApi, options);
this.exportCompileCommands(project, from, to, platform, vrApi, options);
}
exportMakefile(project: Project, from: string, to: string, platform: string, vrApi: any, options: any) {
const cCompiler = Options.compiler === Compiler.GCC ? 'gcc' : 'clang';
const cppCompiler = Options.compiler === Compiler.GCC ? 'g++' : 'clang++';
let objects: any = {};
let ofiles: any = {};
let outputPath = path.resolve(to, options.buildPath);
fs.ensureDirSync(outputPath);
for (let fileobject of project.getFiles()) {
let file = fileobject.file;
if (file.endsWith('.cpp') || file.endsWith('.c') || file.endsWith('.cc') || file.endsWith('.s') || file.endsWith('.S')) {
let name = file.toLowerCase();
if (name.indexOf('/') >= 0) name = name.substr(name.lastIndexOf('/') + 1);
name = name.substr(0, name.lastIndexOf('.'));
if (!objects[name]) {
objects[name] = true;
ofiles[file] = name;
}
else {
while (objects[name]) {
name = name + '_';
}
objects[name] = true;
ofiles[file] = name;
}
}
}
let gchfilelist = '';
let precompiledHeaders: string[] = [];
for (let file of project.getFiles()) {
if (file.options && file.options.pch && precompiledHeaders.indexOf(file.options.pch) < 0) {
precompiledHeaders.push(file.options.pch);
}
}
for (let file of project.getFiles()) {
let precompiledHeader: string = null;
for (let header of precompiledHeaders) {
if (file.file.endsWith(header)) {
precompiledHeader = header;
break;
}
}
if (precompiledHeader !== null) {
// let realfile = path.relative(outputPath, path.resolve(from, file.file));
gchfilelist += path.basename(file.file) + '.gch ';
}
}
let ofilelist = '';
for (let o in objects) {
ofilelist += o + '.o ';
}
this.writeFile(path.resolve(outputPath, 'makefile'));
let incline = '-I./ '; // local directory to pick up the precompiled header hxcpp.h.gch
for (let inc of project.getIncludeDirs()) {
inc = path.relative(outputPath, path.resolve(from, inc));
incline += '-I' + inc + ' ';
}
incline += '-I/usr/local/include'; // Add search dir for FreeBSD
this.p('INC=' + incline);
let libsline = '-static-libgcc -static-libstdc++ -pthread';
/*if (project.cmd) {
libsline += ' -static';
}*/
for (let lib of project.getLibs()) {
libsline += ' -l' + lib;
}
libsline += ' -L/usr/local/lib'; // Add search dir for FreeBSD
this.p('LIB=' + libsline);
let defline = '';
for (const def of project.getDefines()) {
if (def.config && def.config.toLowerCase() === 'debug' && !options.debug) {
continue;
}
if (def.config && def.config.toLowerCase() === 'release' && options.debug) {
continue;
}
defline += '-D' + def.value + ' ';
}
if (!options.debug) {
defline += '-DNDEBUG ';
}
this.p('DEF=' + defline);
this.p();
let cline = '-std=c99 ';
if (project.cStd !== '') {
cline = '-std=' + project.cStd + ' ';
}
if (options.dynlib) {
cline += '-fPIC ';
}
for (let flag of project.cFlags) {
cline += flag + ' ';
}
this.p('CFLAGS=' + cline);
let cppline = '';
if (project.cppStd !== '') {
cppline = '-std=' + project.cppStd + ' ';
}
if (options.dynlib) {
cppline += '-fPIC ';
}
for (let flag of project.cppFlags) {
cppline += flag + ' ';
}
this.p('CPPFLAGS=' + cppline);
let optimization = '';
if (!options.debug) optimization = '-O2';
else optimization = '-g';
let executableName = project.getSafeName();
if (project.getExecutableName()) {
executableName = project.getExecutableName();
}
if (options.lib) {
this.p(executableName + '.a: ' + gchfilelist + ofilelist);
}
else if (options.dynlib) {
this.p(executableName + '.so: ' + gchfilelist + ofilelist);
}
else {
this.p(executableName + ': ' + gchfilelist + ofilelist);
}
let cpp = '';
let output = '-o "' + executableName + '"';
if (options.lib) {
output = '-o "' + executableName + '.a"';
}
else if (options.dynlib) {
output = '-shared -o "' + executableName + '.so"';
}
this.p('\t' + (options.lib ? 'ar rcs' : cppCompiler) + ' ' + output + ' ' + cpp + ' ' + optimization + ' ' + ofilelist + ' $(LIB)');
for (let file of project.getFiles()) {
let precompiledHeader: string = null;
for (let header of precompiledHeaders) {
if (file.file.endsWith(header)) {
precompiledHeader = header;
break;
}
}
if (precompiledHeader !== null) {
let realfile = path.relative(outputPath, path.resolve(from, file.file));
this.p(path.basename(realfile) + '.gch: ' + realfile);
this.p('\t' + cppCompiler + ' ' + cpp + ' ' + optimization + ' $(INC) $(DEF) -c ' + realfile + ' -o ' + path.basename(file.file) + '.gch');
}
}
for (let fileobject of project.getFiles()) {
let file = fileobject.file;
if (file.endsWith('.c') || file.endsWith('.cpp') || file.endsWith('.cc') || file.endsWith('.s') || file.endsWith('.S')) {
if (fileobject.options && fileobject.options.nocompile) {
continue;
}
this.p();
let name = ofiles[file];
let realfile = path.relative(outputPath, path.resolve(from, file));
this.p(name + '.o: ' + realfile);
let compiler = cppCompiler;
let flags = '$(CPPFLAGS)';
if (file.endsWith('.c')) {
compiler = cCompiler;
flags = '$(CFLAGS)';
}
else if (file.endsWith('.s') || file.endsWith('.S')) {
compiler = cCompiler;
flags = '';
}
this.p('\t' + compiler + ' ' + cpp + ' ' + optimization + ' $(INC) $(DEF) ' + flags + ' -c ' + realfile + ' -o ' + name + '.o');
}
}
// project.getDefines()
// project.getIncludeDirs()
this.closeFile();
}
exportCodeBlocks(project: Project, from: string, to: string, platform: string, vrApi: any, options: any) {
this.writeFile(path.resolve(to, project.getSafeName() + '.cbp'));
this.p('<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>');
this.p('<CodeBlocks_project_file>');
this.p('<FileVersion major="1" minor="6" />', 1);
this.p('<Project>', 1);
this.p('<Option title="' + project.getName() + '" />', 2);
this.p('<Option pch_mode="2" />', 2);
this.p('<Option compiler="gcc" />', 2);
this.p('<Build>', 2);
this.p('<Target title="Debug">', 3);
this.p('<Option output="bin/Debug/' + project.getSafeName() + '" prefix_auto="1" extension_auto="1" />', 4);
if (project.getDebugDir().length > 0) this.p('<Option working_dir="' + path.resolve(from, project.getDebugDir()) + '" />', 4);
this.p('<Option object_output="obj/Debug/" />', 4);
this.p('<Option type="1" />', 4);
this.p('<Option compiler="gcc" />', 4);
this.p('<Compiler>', 4);
if (project.cppStd !== '') {
this.p('<Add option="-std=' + project.cppStd + '" />', 5);
}
this.p('<Add option="-g" />', 5);
this.p('</Compiler>', 4);
this.p('</Target>', 3);
this.p('<Target title="Release">', 3);
this.p('<Option output="bin/Release/' + project.getSafeName() + '" prefix_auto="1" extension_auto="1" />', 4);
if (project.getDebugDir().length > 0) this.p('<Option working_dir="' + path.resolve(from, project.getDebugDir()) + '" />', 4);
this.p('<Option object_output="obj/Release/" />', 4);
this.p('<Option type="0" />', 4);
this.p('<Option compiler="gcc" />', 4);
this.p('<Compiler>', 4);
if (project.cppStd !== '') {
this.p('<Add option="-std=' + project.cppStd + '" />', 5);
}
this.p('<Add option="-O2" />', 5);
this.p('</Compiler>', 4);
this.p('<Linker>', 4);
this.p('<Add option="-s" />', 5);
this.p('</Linker>', 4);
this.p('</Target>', 3);
this.p('</Build>', 2);
this.p('<Compiler>', 2);
if (project.cppStd !== '') {
this.p('<Add option="-std=' + project.cppStd + '" />', 5);
}
this.p('<Add option="-Wall" />', 3);
for (const def of project.getDefines()) {
this.p('<Add option="-D' + def.value.replace(/\"/g, '\\"') + '" />', 3);
}
for (let inc of project.getIncludeDirs()) {
this.p('<Add directory="' + path.resolve(from, inc) + '" />', 3);
}
this.p('</Compiler>', 2);
this.p('<Linker>', 2);
this.p('<Add option="-pthread" />', 3);
this.p('<Add option="-static-libgcc" />', 3);
this.p('<Add option="-static-libstdc++" />', 3);
/*if (project.cmd) {
this.p('<Add option="-static" />', 3);
}*/
this.p('<Add option="-Wl,-rpath,." />', 3);
for (let lib of project.getLibs()) {
this.p('<Add library="' + lib + '" />', 3);
}
if (platform === Platform.Pi) {
this.p('<Add directory="/opt/vc/lib" />', 3);
}
this.p('</Linker>', 2);
let precompiledHeaders: string[] = [];
for (let file of project.getFiles()) {
if (file.options && file.options.pch && precompiledHeaders.indexOf(file.options.pch) < 0) {
precompiledHeaders.push(file.options.pch);
}
}
for (let file of project.getFiles()) {
let precompiledHeader: string = null;
for (let header of precompiledHeaders) {
if (file.file.endsWith(header)) {
precompiledHeader = header;
break;
}
}
if (file.file.endsWith('.c') || file.file.endsWith('.cc') || file.file.endsWith('.cpp')) {
this.p('<Unit filename="' + path.resolve(from, file.file) + '">', 2);
this.p('<Option compilerVar="CC" />', 3);
this.p('</Unit>', 2);
}
else if (file.file.endsWith('.h')) {
this.p('<Unit filename="' + path.resolve(from, file.file) + '">', 2);
if (precompiledHeader !== null) {
this.p('<Option compile="1" />', 3);
this.p('<Option weight="0" />', 3);
}
this.p('</Unit>', 2);
}
}
this.p('<Extensions>', 2);
this.p('<code_completion />', 3);
this.p('<debugger />', 3);
this.p('</Extensions>', 2);
this.p('</Project>', 1);
this.p('</CodeBlocks_project_file>');
this.closeFile();
}
exportCompileCommands(project: Project, _from: string, to: string, platform: string, vrApi: any, options: any) {
let from = path.resolve(process.cwd(), _from);
// TODO : assembly files, precompiled headers and all that stuff
// compile_commands.json is primarily for code completion so those things shouldn't matter too much
this.writeFile(path.resolve(to, 'compile_commands.json'));
let includes = [];
for (let inc of project.getIncludeDirs()) {
includes.push('-I');
includes.push(path.resolve(from, inc));
}
let defines = [];
for (let def of project.getDefines()) {
defines.push('-D');
defines.push(def.value.replace(/\"/g, '\\"'));
}
let libs = [];
for (let lib of project.getLibs()) {
libs.push('-l' + lib);
}
let optimization = options.debug ? '-g' : '-O2';
let objects: any = {};
let ofiles: any = {};
for (let fileobject of project.getFiles()) {
let file = fileobject.file;
if (file.endsWith('.cpp') || file.endsWith('.c') || file.endsWith('.cc') || file.endsWith('.s') || file.endsWith('.S')) {
let name = file.toLowerCase();
if (name.indexOf('/') >= 0) name = name.substr(name.lastIndexOf('/') + 1);
name = name.substr(0, name.lastIndexOf('.'));
if (!objects[name]) {
objects[name] = true;
ofiles[file] = name;
}
else {
while (objects[name]) {
name = name + '_';
}
objects[name] = true;
ofiles[file] = name;
}
}
}
let commands = [];
for (let fileobject of project.getFiles()) {
let file = fileobject.file;
if (file.endsWith('.c') || file.endsWith('.cpp') || file.endsWith('.cc')) {
let args = [file.endsWith('.c') ? '/usr/bin/clang' : '/usr/bin/clang++', optimization, '-c', '-o', (options.debug ? 'debug' : 'release') + ofiles[file] + '.o'];
if (file.endsWith('.c')) {
args.push('-std=' + (project.cStd !== '' ? project.cStd : 'c99'));
}
else if (file.endsWith('.cpp')) {
args.push('-std=' + (project.cppStd !== '' ? project.cppStd : 'c++11'));
}
if (options.dynlib) {
args.push('-fPIC');
}
args.push(path.resolve(from, file));
let command = {
directory: from,
file: path.resolve(from, file),
output: path.resolve(to, ofiles[file] + '.o'),
arguments: args.concat(includes).concat(defines)
};
commands.push(command);
}
}
/*let linker_args = ['/usr/bin/clang', '-O2', '-static-libgcc', '-static-libstdc++', '-pthread'];
for (let file in ofiles) {
linker_args.push(path.resolve(to, ofiles[file] + '.o'));
}
linker_args.push('-o');
if (options.lib) {
linker_args.push('-o');
linker_args.push(project.getSafeName() + '.a');
}
else if (options.dynlib) {
linker_args.push('-shared');
linker_args.push('-fPIC');
linker_args.push('-o');
linker_args.push(project.getSafeName() + '.so');
}
else {
linker_args.push('-o');
linker_args.push(project.getSafeName());
}
commands.push({
directory: from,
output: linker_args[linker_args.length - 1],
arguments: linker_args.concat(libs)
});*/
this.p(JSON.stringify(commands));
this.closeFile();
}
}

View File

@ -0,0 +1,128 @@
import { Exporter } from 'kmake/Exporters/Exporter';
import { Project } from 'kmake/Project';
import * as path from 'path';
export class JsonExporter extends Exporter {
constructor(options: any) {
super(options);
}
async exportSolution(project: Project, from: string, to: string, platform: string, vrApi: any, options: any) {
if (options.stdout) {
this.writeStdout();
}
else {
this.writeFile(path.resolve(to, project.getSafeName() + '.json'));
}
this.p('{');
this.p('"includes":', 1);
this.p('[', 1);
for (let i = 0; i < project.getIncludeDirs().length; ++i) {
const inc = path.relative(from, project.getIncludeDirs()[i]);
if (i === project.getIncludeDirs().length - 1) {
this.p('"' + inc.replace(/\\/g, '\\\\') + '"', 2);
}
else {
this.p('"' + inc.replace(/\\/g, '\\\\') + '",', 2);
}
}
this.p('],', 1);
this.p('"libraries":', 1);
this.p('[', 1);
for (let i = 0; i < project.getLibs().length; ++i) {
const lib = project.getLibs()[i];
if (i === project.getLibs().length - 1) {
this.p('"' + lib + '"', 2);
}
else {
this.p('"' + lib + '",', 2);
}
}
this.p('],', 1);
this.p('"defines":', 1);
this.p('[', 1);
for (let i = 0; i < project.getDefines().length; ++i) {
const def = project.getDefines()[i];
if (i === project.getDefines().length - 1) {
this.p('"' + def.value + '"', 2);
}
else {
this.p('"' + def.value + '",', 2);
}
}
this.p('],', 1);
this.p('"files":', 1);
this.p('[', 1);
for (let i = 0; i < project.getFiles().length; ++i) {
const file = project.getFiles()[i].file;
const realfile = path.relative(from, file);
if (i === project.getFiles().length - 1) {
this.p('"' + realfile.replace(/\\/g, '\\\\') + '"', 2);
}
else {
this.p('"' + realfile.replace(/\\/g, '\\\\') + '",', 2);
}
}
this.p('],', 1);
this.p('"kongDirs":', 1);
this.p('[', 1);
for (let i = 0; i < project.getKongDirs().length; ++i) {
const kongDir = project.getKongDirs()[i];
if (i === project.getKongDirs().length - 1) {
this.p('"' + kongDir + '"', 2);
}
else {
this.p('"' + kongDir + '",', 2);
}
}
this.p('],', 1);
this.p('"name": "' + project.getName() + '",', 1);
this.p('"cStd": "' + project.getCStd() + '",', 1);
this.p('"cFlags":', 1);
this.p('[', 1);
for (let i = 0; i < project.cFlags.length; ++i) {
const cFlag = project.cFlags[i];
if (i === project.cFlags.length - 1) {
this.p('"' + cFlag + '"', 2);
}
else {
this.p('"' + cFlag + '",', 2);
}
}
this.p('],', 1);
this.p('"cppStd": "' + project.getCppStd() + '",', 1);
this.p('"cppFlags":', 1);
this.p('[', 1);
for (let i = 0; i < project.cppFlags.length; ++i) {
const cppFlag = project.cppFlags[i];
if (i === project.cppFlags.length - 1) {
this.p('"' + cppFlag + '"', 2);
}
else {
this.p('"' + cppFlag + '",', 2);
}
}
this.p('],', 1);
this.p('"debugDir": "' + project.getDebugDir().replace(/\\/g, '\\\\') + '"', 1);
this.p('}')
if (options.stdout) {
this.closeStdout();
}
else {
this.closeFile();
}
}
}

View File

@ -0,0 +1,41 @@
import { Exporter } from 'kmake/Exporters/Exporter';
import { Project } from 'kmake/Project';
import { NinjaExporter } from 'kmake/Exporters/NinjaExporter';
import { MakeExporter } from 'kmake/Exporters/MakeExporter';
import { CLionExporter } from 'kmake/Exporters/CLionExporter';
import { CompilerCommandsExporter } from 'kmake/Exporters/CompileCommandsExporter';
import * as fs from 'kmake/fsextra';
import * as path from 'path';
export class KompjutaExporter extends Exporter {
ninja: NinjaExporter;
make: MakeExporter;
clion: CLionExporter;
compileCommands: CompilerCommandsExporter;
constructor(options: any) {
super(options);
const cFlags = '--target=riscv64-unknown-elf -march=rv64imfdv_zvl1024b -mabi=lp64d -Os -ffreestanding -fno-builtin -ffunction-sections -fdata-sections -nostdlib -mno-relax';
const linkerFlags = '--target=riscv64-unknown-elf -march=rv64imfdv_zvl1024b -mabi=lp64d -Os -ffreestanding -fno-builtin -ffunction-sections -fdata-sections -nostdlib -mno-relax -fuse-ld=lld "-Wl,--no-relax,--gc-sections,-Map,link.map,-T,kompjuta.ld"';
const outputExtension = '.elf';
this.ninja = new NinjaExporter(options, 'clang', 'clang++', cFlags, cFlags, linkerFlags, outputExtension);
this.make = new MakeExporter(options, 'clang', 'clang++', cFlags, cFlags, linkerFlags, outputExtension);
this.clion = new CLionExporter(options);
this.compileCommands = new CompilerCommandsExporter(options);
}
async exportSolution(project: Project, from: string, to: string, platform: string, vrApi: any, options: any) {
const textData = require('fs').getEmbeddedData();
let outputPath = path.resolve(to, options.buildPath);
fs.ensureDirSync(outputPath);
fs.writeFileSync(path.join(outputPath, 'kompjuta.ld'), textData['kompjuta_kompjuta_ld']);
this.ninja.exportSolution(project, from, to, platform, vrApi, options);
this.make.exportSolution(project, from, to, platform, vrApi, options);
this.clion.exportSolution(project, from, to, platform, vrApi, options);
this.compileCommands.exportSolution(project, from, to, platform, vrApi, options);
}
}

View File

@ -0,0 +1,232 @@
import { Exporter } from 'kmake/Exporters/Exporter';
import { Options } from 'kmake/Options';
import { Platform } from 'kmake/Platform';
import { Project } from 'kmake/Project';
import { Compiler } from 'kmake/Compiler';
import * as fs from 'kmake/fsextra';
import * as path from 'path';
import { NinjaExporter } from 'kmake/Exporters/NinjaExporter';
import { MakeExporter } from 'kmake/Exporters/MakeExporter';
import { CLionExporter } from 'kmake/Exporters/CLionExporter';
import { CompilerCommandsExporter } from 'kmake/Exporters/CompileCommandsExporter';
export class LinuxExporter extends Exporter {
ninja: NinjaExporter;
make: MakeExporter;
clion: CLionExporter;
compileCommands: CompilerCommandsExporter;
constructor(options: any) {
super(options);
let linkerFlags = '-static-libgcc -static-libstdc++ -pthread';
if (Options.compiler === Compiler.MuslGcc || this.getOS().includes('Alpine')) {
linkerFlags += ' -static';
}
let outputExtension = '';
if (options.lib) {
outputExtension = '.a';
}
else if (options.dynlib) {
outputExtension = '.so';
}
this.ninja = new NinjaExporter(options, this.getCCompiler(), this.getCPPCompiler(), '', '', linkerFlags, outputExtension);
this.make = new MakeExporter(options, this.getCCompiler(), this.getCPPCompiler(), '', '', linkerFlags, outputExtension);
this.clion = new CLionExporter(options);
this.compileCommands = new CompilerCommandsExporter(options);
}
async exportSolution(project: Project, from: string, to: string, platform: string, vrApi: any, options: any) {
this.ninja.exportSolution(project, from, to, platform, vrApi, options);
this.make.exportSolution(project, from, to, platform, vrApi, options);
this.exportCodeBlocks(project, from, to, platform, vrApi, options);
this.clion.exportSolution(project, from, to, platform, vrApi, options);
this.compileCommands.exportSolution(project, from, to, platform, vrApi, options);
}
getCCompiler(): string {
switch (Options.compiler) {
case Compiler.Default:
case Compiler.GCC:
return 'gcc';
case Compiler.Clang:
return 'clang';
case Compiler.MuslGcc:
return 'musl-gcc';
case Compiler.Custom:
return Options.ccPath;
default:
throw 'Unsupported compiler ' + Options.compiler;
}
}
getCPPCompiler(): string {
switch (Options.compiler) {
case Compiler.Default:
case Compiler.GCC:
return 'g++';
case Compiler.Clang:
return 'clang++';
case Compiler.MuslGcc:
return 'g++';
case Compiler.Custom:
return Options.cxxPath;
default:
throw 'Unsupported compiler ' + Options.compiler;
}
}
getOS(): string {
try {
const data = fs.readFileSync('/etc/os-release', 'utf8');
const lines = data.split('\n');
let name = null;
for (const line of lines) {
if (line.trim().startsWith('NAME')) {
name = line.split('=')[1];
name = name.substring(1, name.length - 1);
break;
}
}
if (name) {
return name;
}
else {
return 'Unknown';
}
}
catch (error) {
return 'Unknown';
}
}
exportCodeBlocks(project: Project, from: string, to: string, platform: string, vrApi: any, options: any) {
this.writeFile(path.resolve(to, project.getSafeName() + '.cbp'));
this.p('<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>');
this.p('<CodeBlocks_project_file>');
this.p('<FileVersion major="1" minor="6" />', 1);
this.p('<Project>', 1);
this.p('<Option title="' + project.getName() + '" />', 2);
this.p('<Option pch_mode="2" />', 2);
this.p('<Option compiler="gcc" />', 2);
this.p('<Build>', 2);
this.p('<Target title="Debug">', 3);
let executableName = project.getSafeName();
if (project.getExecutableName()) {
executableName = project.getExecutableName();
}
this.p('<Option output="bin/Debug/' + executableName + '" prefix_auto="1" extension_auto="1" />', 4);
if (project.getDebugDir().length > 0) this.p('<Option working_dir="' + path.resolve(from, project.getDebugDir()) + '" />', 4);
this.p('<Option object_output="obj/Debug/" />', 4);
this.p('<Option type="1" />', 4);
this.p('<Option compiler="gcc" />', 4);
this.p('<Compiler>', 4);
if (project.cppStd !== '') {
this.p('<Add option="-std=' + project.cppStd + '" />', 5);
}
this.p('<Add option="-g" />', 5);
for (const def of project.getDefines()) {
if (def.config && def.config.toLowerCase() !== 'debug') {
continue;
}
this.p('<Add option="-D' + def.value.replace(/\"/g, '\\"') + '" />', 3);
}
this.p('</Compiler>', 4);
this.p('</Target>', 3);
this.p('<Target title="Release">', 3);
this.p('<Option output="bin/Release/' + executableName + '" prefix_auto="1" extension_auto="1" />', 4);
if (project.getDebugDir().length > 0) this.p('<Option working_dir="' + path.resolve(from, project.getDebugDir()) + '" />', 4);
this.p('<Option object_output="obj/Release/" />', 4);
this.p('<Option type="0" />', 4);
this.p('<Option compiler="gcc" />', 4);
this.p('<Compiler>', 4);
if (project.cppStd !== '') {
this.p('<Add option="-std=' + project.cppStd + '" />', 5);
}
this.p('<Add option="-O2" />', 5);
for (const def of project.getDefines()) {
if (def.config && def.config.toLowerCase() !== 'release') {
continue;
}
this.p('<Add option="-D' + def.value.replace(/\"/g, '\\"') + '" />', 3);
}
this.p('<Add option="-DNDEBUG" />', 3);
this.p('</Compiler>', 4);
this.p('<Linker>', 4);
this.p('<Add option="-s" />', 5);
this.p('</Linker>', 4);
this.p('</Target>', 3);
this.p('</Build>', 2);
this.p('<Compiler>', 2);
if (project.cppStd !== '') {
this.p('<Add option="-std=' + project.cppStd + '" />', 5);
}
this.p('<Add option="-Wall" />', 3);
for (let inc of project.getIncludeDirs()) {
this.p('<Add directory="' + path.resolve(from, inc) + '" />', 3);
}
this.p('</Compiler>', 2);
this.p('<Linker>', 2);
this.p('<Add option="-pthread" />', 3);
this.p('<Add option="-static-libgcc" />', 3);
this.p('<Add option="-static-libstdc++" />', 3);
/*if (project.cmd) {
this.p('<Add option="-static" />', 3);
}*/
this.p('<Add option="-Wl,-rpath,." />', 3);
const flags = [].concat(project.cFlags, project.cppFlags);
for (const flag of flags) {
this.p('<Add option="' + flag.replace(/\"/g, '\\"') + '" />', 3);
}
for (let lib of project.getLibs()) {
this.p('<Add library="' + lib + '" />', 3);
}
if (platform === Platform.Pi) {
this.p('<Add directory="/opt/vc/lib" />', 3);
}
this.p('</Linker>', 2);
let precompiledHeaders: string[] = [];
for (let file of project.getFiles()) {
if (file.options && file.options.pch && precompiledHeaders.indexOf(file.options.pch) < 0) {
precompiledHeaders.push(file.options.pch);
}
}
for (let file of project.getFiles()) {
let precompiledHeader: string = null;
for (let header of precompiledHeaders) {
if (file.file.endsWith(header)) {
precompiledHeader = header;
break;
}
}
if (file.file.endsWith('.c') || file.file.endsWith('.cc') || file.file.endsWith('.cpp')) {
this.p('<Unit filename="' + path.resolve(from, file.file) + '">', 2);
if (!file.options || !file.options.nocompile) {
this.p('<Option compilerVar="CC" />', 3);
}
this.p('</Unit>', 2);
}
else if (file.file.endsWith('.h')) {
this.p('<Unit filename="' + path.resolve(from, file.file) + '">', 2);
if (precompiledHeader !== null) {
this.p('<Option compile="1" />', 3);
this.p('<Option weight="0" />', 3);
}
this.p('</Unit>', 2);
}
}
this.p('<Extensions>', 2);
this.p('<code_completion />', 3);
this.p('<debugger />', 3);
this.p('</Extensions>', 2);
this.p('</Project>', 1);
this.p('</CodeBlocks_project_file>');
this.closeFile();
}
}

View File

@ -0,0 +1,225 @@
import { Exporter } from 'kmake/Exporters/Exporter';
import { Project } from 'kmake/Project';
import * as fs from 'kmake/fsextra';
import * as path from 'path';
export class MakeExporter extends Exporter {
cCompiler: string;
cppCompiler: string;
cFlags: string;
cppFlags: string;
linkerFlags: string;
outputExtension: string;
constructor(options: any, cCompiler: string, cppCompiler: string, cFlags: string, cppFlags: string, linkerFlags: string, outputExtension: string, libsLine: (p: Project) => string = null) {
super(options);
this.cCompiler = cCompiler;
this.cppCompiler = cppCompiler;
this.cFlags = cFlags;
this.cppFlags = cppFlags;
this.linkerFlags = linkerFlags;
this.outputExtension = outputExtension;
if (libsLine != null) {
this.libsLine = libsLine;
}
}
libsLine(project: Project): string {
let libs = '';
for (let lib of project.getLibs()) {
libs += ' -l' + lib;
}
return libs;
}
async exportSolution(project: Project, from: string, to: string, platform: string, vrApi: any, options: any) {
let objects: any = {};
let ofiles: any = {};
let outputPath = path.resolve(to, options.buildPath);
fs.ensureDirSync(outputPath);
for (let fileobject of project.getFiles()) {
let file = fileobject.file;
if (file.endsWith('.cpp') || file.endsWith('.c') || file.endsWith('.cc') || file.endsWith('.s') || file.endsWith('.S')) {
if (fileobject.options && fileobject.options.nocompile) {
continue;
}
let name = file.toLowerCase();
if (name.indexOf('/') >= 0) name = name.substr(name.lastIndexOf('/') + 1);
name = name.substr(0, name.lastIndexOf('.'));
if (!objects[name]) {
objects[name] = true;
ofiles[file] = name;
}
else {
while (objects[name]) {
name = name + '_';
}
objects[name] = true;
ofiles[file] = name;
}
}
}
let gchfilelist = '';
let precompiledHeaders: string[] = [];
for (let file of project.getFiles()) {
if (file.options && file.options.pch && precompiledHeaders.indexOf(file.options.pch) < 0) {
precompiledHeaders.push(file.options.pch);
}
}
for (let file of project.getFiles()) {
let precompiledHeader: string = null;
for (let header of precompiledHeaders) {
if (file.file.endsWith(header)) {
precompiledHeader = header;
break;
}
}
if (precompiledHeader !== null) {
// let realfile = path.relative(outputPath, path.resolve(from, file.file));
gchfilelist += path.basename(file.file) + '.gch ';
}
}
let ofilelist = '';
for (let o in objects) {
ofilelist += o + '.o ';
}
this.writeFile(path.resolve(outputPath, 'makefile'));
let incline = '-I./ '; // local directory to pick up the precompiled headers
for (let inc of project.getIncludeDirs()) {
inc = path.relative(outputPath, path.resolve(from, inc));
incline += '-I' + inc + ' ';
}
this.p('INC=' + incline);
let linkerline = this.linkerFlags;
linkerline += this.libsLine(project);
linkerline += ' ';
for (let flag of project.linkerFlags) {
linkerline += flag + ' ';
}
this.p('LIB=' + linkerline);
let defline = '';
for (const def of project.getDefines()) {
if (def.config && def.config.toLowerCase() === 'debug' && !options.debug) {
continue;
}
if (def.config && def.config.toLowerCase() === 'release' && options.debug) {
continue;
}
defline += '-D' + def.value.replace(/\"/g, '\\"') + ' ';
}
if (!options.debug) {
defline += '-DNDEBUG ';
}
this.p('DEF=' + defline);
this.p();
let cline = this.cFlags;
if (project.cStd !== '') {
cline = '-std=' + project.cStd + ' ';
}
if (options.dynlib) {
cline += '-fPIC ';
}
for (let flag of project.cFlags) {
cline += flag + ' ';
}
this.p('CFLAGS=' + cline);
let cppline = this.cppFlags;
if (project.cppStd !== '') {
cppline = '-std=' + project.cppStd + ' ';
}
if (options.dynlib) {
cppline += '-fPIC ';
}
for (let flag of project.cppFlags) {
cppline += flag + ' ';
}
this.p('CPPFLAGS=' + cppline);
let optimization = '';
if (!options.debug) {
optimization = '-O2';
}
else optimization = '-g';
let executableName = project.getSafeName();
if (project.getExecutableName()) {
executableName = project.getExecutableName();
}
let outputname = this.outputExtension === '.html' ? 'index.html' : executableName + this.outputExtension;
this.p(outputname + ': ' + gchfilelist + ofilelist);
let output = '-o "' + outputname + '"';
if (options.dynlib) {
output = '-shared -o "' + executableName + '.so"';
}
if (options.lib) {
this.p('\t' + 'ar rcs ' + output + ' ' + ofilelist);
}
else {
this.p('\t' + this.cppCompiler + ' ' + output + ' ' + optimization + ' ' + ofilelist + ' $(LIB)');
}
for (let file of project.getFiles()) {
let precompiledHeader: string = null;
for (let header of precompiledHeaders) {
if (file.file.endsWith(header)) {
precompiledHeader = header;
break;
}
}
if (precompiledHeader !== null) {
let realfile = path.relative(outputPath, path.resolve(from, file.file));
this.p('-include ' + path.basename(file.file) + '.d');
this.p(path.basename(realfile) + '.gch: ' + realfile);
this.p('\t' + this.cppCompiler + ' ' + optimization + ' $(INC) $(DEF) -MD -c ' + realfile + ' -o ' + path.basename(file.file) + '.gch');
}
}
for (let fileobject of project.getFiles()) {
let file = fileobject.file;
if (file.endsWith('.c') || file.endsWith('.cpp') || file.endsWith('.cc') || file.endsWith('.s') || file.endsWith('.S')) {
if (fileobject.options && fileobject.options.nocompile) {
continue;
}
this.p();
let name = ofiles[file];
let realfile = path.relative(outputPath, path.resolve(from, file));
this.p('-include ' + name + '.d');
this.p(name + '.o: ' + realfile);
let compiler = this.cppCompiler;
let flags = '$(CPPFLAGS)';
if (file.endsWith('.c')) {
compiler = this.cCompiler;
flags = '$(CFLAGS)';
}
else if (file.endsWith('.s') || file.endsWith('.S')) {
compiler = this.cCompiler;
flags = '';
}
this.p('\t' + compiler + ' ' + optimization + ' $(INC) $(DEF) -MD ' + flags + ' -c ' + realfile + ' -o ' + name + '.o');
}
}
this.closeFile();
}
}

View File

@ -0,0 +1,343 @@
import { Project } from 'kmake/Project';
import { Platform } from 'kmake/Platform';
import * as path from 'path';
import { Exporter } from 'kmake/Exporters/Exporter';
export class MesonExporter extends Exporter {
constructor(options: any) {
super(options);
}
async exportSolution(project: Project, from: string, to: string, platform: string, vrApi: any, options: any): Promise<void> {
let name = project.getSafeName();
this.writeFile(path.resolve(to, 'meson.build'));
let includeDirs = [...project.getIncludeDirs()];
let systemOs = '';
if (platform === Platform.Windows || platform === Platform.WindowsApp) {
systemOs = 'windows';
} else if (platform === Platform.OSX) {
systemOs = 'macos';
} else if (platform === Platform.iOS) {
systemOs = 'ios';
} else if (platform === Platform.tvOS) {
systemOs = 'tvos';
} else if (platform === Platform.Linux) {
systemOs = 'linux';
} else if (platform === Platform.Android) {
systemOs = 'android';
}
if (includeDirs.length > 0) {
this.p('kore_inc_dirs = include_directories(');
for (let i = 0; i < includeDirs.length; i++) {
const inc = this.nicePath(from, to, includeDirs[i]);
const comma = i === includeDirs.length - 1 ? '' : ',';
this.p(' \'' + inc.replace(/\\/g, '/') + '\'' + comma, 1);
}
this.p(')');
this.p();
}
let defineArgs: string[] = [];
if (project.getDefines().length > 0) {
for (const def of project.getDefines()) {
if (def.config && def.config.toLowerCase() === 'debug' && !options.debug) {
continue;
}
if (def.config && def.config.toLowerCase() === 'release' && options.debug) {
continue;
}
defineArgs.push('-D' + def.value);
}
}
if (!options.debug) {
defineArgs.push('-DNDEBUG');
}
if (project.cFlags.length > 0 || defineArgs.length > 0) {
this.p('kore_c_args = [');
for (const flag of project.cFlags) {
this.p(' \'' + flag + '\',', 1);
}
for (const def of defineArgs) {
this.p(' \'' + def + '\',', 1);
}
this.p(']');
this.p();
}
if (project.cppFlags.length > 0 || defineArgs.length > 0) {
this.p('kore_cpp_args = [');
for (const flag of project.cppFlags) {
this.p(' \'' + flag + '\',', 1);
}
for (const def of defineArgs) {
this.p(' \'' + def + '\',', 1);
}
this.p(']');
this.p();
}
let sourceFiles = project.getFiles().filter(file => {
if (file.options && file.options.nocompile) {
return false;
}
for (let exclude of project.excludes) {
if (project.matches(file.file, exclude)) {
console.log(`Excluding file ${file.file} due to exclude pattern: ${exclude}`);
return false;
}
}
const ext = path.extname(file.file).toLowerCase();
let validExtensions = ['.c', '.cpp', '.cc', '.cxx'];
if (platform === Platform.iOS || platform === Platform.OSX || platform === Platform.tvOS) {
validExtensions.push('.m', '.mm');
}
return validExtensions.includes(ext);
});
let metalFiles = project.getFiles().filter(file => {
if (file.options && file.options.nocompile) {
return false;
}
const ext = path.extname(file.file).toLowerCase();
return ext === '.metal';
});
let headerFiles = project.getFiles().filter(file => {
const ext = path.extname(file.file).toLowerCase();
return ['.h', '.hpp', '.hxx'].includes(ext);
});
if (sourceFiles.length > 0) {
this.p('sources = [');
for (const fileObj of sourceFiles) {
const relativePath = this.nicePath(from, to, fileObj.file);
this.p(' \'' + relativePath.replace(/\\/g, '/') + '\',', 1);
}
this.p(']');
this.p();
}
let allFiles = [];
if (sourceFiles.length > 0) {
allFiles.push('sources');
}
if (allFiles.length > 0) {
this.p('target_files = ' + allFiles.join(' + '));
this.p();
}
if (platform === Platform.iOS || platform === Platform.OSX || platform === Platform.tvOS) {
this.p('add_languages(\'objc\', native: false)');
this.p();
if (defineArgs.length > 0) {
this.p('kore_objc_args = [');
this.p(' \'-fobjc-arc\',', 1);
this.p(']');
this.p();
}
}
if (project.getLibs().length > 0) {
this.p('dependencies = [');
for (const lib of project.getLibs()) {
this.p(' dependency(\'' + lib + '\'),', 1);
}
this.p(']');
this.p();
}
if (project.subProjects.length > 0) {
this.p('subproject_targets = [');
for (const subproject of project.subProjects) {
this.p(' \'' + subproject + '\',', 1);
}
this.p(']');
this.p();
}
if ((platform === Platform.iOS || platform === Platform.OSX || platform === Platform.tvOS) && metalFiles.length > 0) {
this.p('metal_compiler = find_program(\'xcrun\')');
this.p();
for (let i = 0; i < metalFiles.length; i++) {
const metalFile = metalFiles[i];
const relativePath = this.nicePath(from, to, metalFile.file);
const baseName = path.basename(metalFile.file, '.metal');
this.p(baseName + '_air = custom_target(\'' + baseName + '_air\',');
this.p(' output: \'' + baseName + '.air\',', 1);
this.p(' input: \'' + relativePath.replace(/\\/g, '/') + '\',', 1);
this.p(' command: [metal_compiler, \'-sdk\', \'macosx\', \'metal\', \'-c\', \'@INPUT@\', \'-o\', \'@OUTPUT@\'],', 1);
this.p(' build_by_default: true', 1);
this.p(')');
this.p();
this.p(baseName + '_metallib = custom_target(\'' + baseName + '_metallib\',');
this.p(' output: \'' + baseName + '.metallib\',', 1);
this.p(' input: ' + baseName + '_air,', 1);
this.p(' command: [metal_compiler, \'-sdk\', \'macosx\', \'metallib\', \'@INPUT@\', \'-o\', \'@OUTPUT@\'],', 1);
this.p(' build_by_default: true', 1);
this.p(')');
this.p();
}
}
let executableName = project.getSafeName();
if (project.getExecutableName()) {
executableName = project.getExecutableName();
}
let targetArgs = [];
if (allFiles.length > 0) {
targetArgs.push('target_files');
}
if (includeDirs.length > 0) {
targetArgs.push('include_directories : kore_inc_dirs');
}
if (project.cFlags.length > 0 || defineArgs.length > 0) {
targetArgs.push('c_args : kore_c_args');
}
if (project.cppFlags.length > 0 || defineArgs.length > 0) {
targetArgs.push('cpp_args : kore_cpp_args');
}
if ((platform === Platform.iOS || platform === Platform.OSX || platform === Platform.tvOS) && defineArgs.length > 0) {
targetArgs.push('objc_args : [kore_objc_args] + kore_c_args');
}
if (project.getLibs().length > 0) {
targetArgs.push('dependencies : dependencies');
}
if (project.subProjects.length > 0) {
targetArgs.push('link_with : subproject_targets');
}
targetArgs.push('install : false');
if (project.linkerFlags.length > 0) {
this.p('link_args = [');
for (const flag of project.linkerFlags) {
this.p(' \'' + flag + '\',', 1);
}
this.p(']');
this.p();
targetArgs.push('link_args : kore_link_args');
}
if (options.lib) {
this.p(executableName + ' = library(\'' + executableName + '\',');
} else if (options.dynlib) {
this.p(executableName + ' = shared_library(\'' + executableName + '\',');
} else {
this.p('executable(\'' + executableName + '\',');
}
for (let i = 0; i < targetArgs.length; i++) {
const comma = i === targetArgs.length - 1 ? '' : ',';
this.p(' ' + targetArgs[i] + comma, 1);
}
this.p(')');
this.closeFile();
}
private mapCStandard(std: string): string {
switch (std) {
case 'c90':
case 'c89':
case 'iso9899:1990':
return 'c89';
case 'gnu90':
case 'gnu89':
return 'gnu89';
case 'c99':
case 'c9x':
case 'iso9899:1999':
return 'c99';
case 'gnu99':
case 'gnu9x':
return 'gnu99';
case 'c11':
case 'c1x':
case 'iso9899:2011':
return 'c11';
case 'gnu11':
case 'gnu1x':
return 'gnu11';
case 'c17':
case 'c18':
case 'iso9899:2017':
case 'iso9899:2018':
return 'c17';
case 'gnu17':
case 'gnu18':
return 'gnu17';
case 'c2x':
case 'c23':
return 'c2x';
case 'gnu2x':
case 'gnu23':
return 'gnu2x';
default:
return 'c99';
}
}
private mapCppStandard(std: string): string {
switch (std) {
case 'c++03':
case 'c++98':
case 'gnu++03':
case 'gnu++98':
return 'c++03';
case 'c++11':
case 'c++0x':
case 'gnu++11':
case 'gnu++0x':
return 'c++11';
case 'c++14':
case 'c++1y':
case 'gnu++14':
case 'gnu++1y':
return 'c++14';
case 'c++17':
case 'c++1z':
case 'gnu++17':
case 'gnu++1z':
return 'c++17';
case 'c++20':
case 'c++2a':
case 'gnu++20':
case 'gnu++2a':
return 'c++20';
case 'c++23':
case 'c++2b':
case 'gnu++23':
case 'gnu++2b':
return 'c++23';
case 'c++26':
case 'c++2c':
case 'gnu++26':
case 'gnu++2c':
return 'c++26';
default:
return 'c++17';
}
}
}

View File

@ -0,0 +1,184 @@
import { Exporter } from 'kmake/Exporters/Exporter';
import { Project } from 'kmake/Project';
import * as fs from 'kmake/fsextra';
import * as path from 'path';
export class NinjaExporter extends Exporter {
cCompiler: string;
cppCompiler: string;
cFlags: string;
cppFlags: string;
linkerFlags: string;
outputExtension: string;
constructor(options: any, cCompiler: string, cppCompiler: string, cFlags: string, cppFlags: string, linkerFlags: string, outputExtension: string, libsLine: (p: Project) => string = null) {
super(options);
this.cCompiler = cCompiler;
this.cppCompiler = cppCompiler;
this.cFlags = cFlags;
this.cppFlags = cppFlags;
this.linkerFlags = linkerFlags;
this.outputExtension = outputExtension;
if (libsLine != null) {
this.libsLine = libsLine;
}
}
libsLine(project: Project): string {
let libs = '';
for (let lib of project.getLibs()) {
libs += ' -l' + lib;
}
return libs;
}
async exportSolution(project: Project, from: string, to: string, platform: string, vrApi: any, options: any) {
let objects: any = {};
let ofiles: any = {};
let outputPath = path.resolve(to, options.buildPath);
fs.ensureDirSync(outputPath);
for (let fileobject of project.getFiles()) {
let file = fileobject.file;
if (file.endsWith('.cpp') || file.endsWith('.c') || file.endsWith('.cc') || file.endsWith('.s') || file.endsWith('.S')) {
if (fileobject.options && fileobject.options.nocompile) {
continue;
}
let name = file.toLowerCase();
if (name.indexOf('/') >= 0) name = name.substr(name.lastIndexOf('/') + 1);
name = name.substr(0, name.lastIndexOf('.'));
if (!objects[name]) {
objects[name] = true;
ofiles[file] = name;
}
else {
while (objects[name]) {
name = name + '_';
}
objects[name] = true;
ofiles[file] = name;
}
}
}
let ofilelist = '';
for (let o in objects) {
ofilelist += o + '.o ';
}
this.writeFile(path.resolve(outputPath, 'build.ninja'));
this.p('pool link_pool\n depth = 1\n');
let incline = '';
for (let inc of project.getIncludeDirs()) {
inc = path.relative(outputPath, path.resolve(from, inc));
incline += '-I' + inc + ' ';
}
let linkerline = this.linkerFlags;
linkerline += this.libsLine(project);
linkerline += ' ';
for (let flag of project.linkerFlags) {
linkerline += flag + ' ';
}
let defline = '';
for (const def of project.getDefines()) {
if (def.config && def.config.toLowerCase() === 'debug' && !options.debug) {
continue;
}
if (def.config && def.config.toLowerCase() === 'release' && options.debug) {
continue;
}
defline += '-D' + def.value.replace(/\"/g, '\\"') + ' ';
}
if (!options.debug) {
defline += '-DNDEBUG ';
}
let optimization = '';
if (!options.debug) {
optimization = '-O2';
}
else optimization = '-g';
let cline = this.cCompiler + ' ' + this.cFlags + ' ';
if (project.cStd !== '') {
cline += '-std=' + project.cStd + ' ';
}
if (options.dynlib) {
cline += '-fPIC ';
}
for (let flag of project.cFlags) {
cline += flag + ' ';
}
cline += optimization + ' ';
cline += incline;
cline += defline;
this.p('rule cc\n deps = gcc\n depfile = $out.d\n command = ' + cline + '-MD -MF $out.d -c $in -o $out\n');
let cppline = this.cppCompiler + ' ' + this.cppFlags + ' ';
if (project.cppStd !== '') {
cppline += '-std=' + project.cppStd + ' ';
}
if (options.dynlib) {
cppline += '-fPIC ';
}
for (let flag of project.cppFlags) {
cppline += flag + ' ';
}
cppline += optimization + ' ';
cppline += incline;
cppline += defline;
this.p('rule cxx\n deps = gcc\n depfile = $out.d\n command = ' + cppline + '-MD -MF $out.d -c $in -o $out\n');
if (options.dynlib) {
this.p('rule link\n pool = link_pool\n command = ' + this.cppCompiler + ' -fPIC -shared -o $out ' + optimization + ' $in ' + linkerline);
}
else if (options.lib) {
this.p('rule link\n pool = link_pool\n command = ar rcs -o $out $in');
}
else {
this.p('rule link\n pool = link_pool\n command = ' + this.cppCompiler + ' -o $out ' + optimization + ' $in ' + linkerline);
}
for (let fileobject of project.getFiles()) {
let file = fileobject.file;
if (file.endsWith('.c') || file.endsWith('.cpp') || file.endsWith('.cc') || file.endsWith('.s') || file.endsWith('.S')) {
if (fileobject.options && fileobject.options.nocompile) {
continue;
}
this.p();
let name = ofiles[file];
let realfile = path.relative(outputPath, path.resolve(from, file));
let compiler = 'cxx';
if (file.endsWith('.c')) {
compiler = 'cc';
}
else if (file.endsWith('.s') || file.endsWith('.S')) {
compiler = 'asm';
}
this.p('build ' + name + '.o: ' + compiler + ' ' + realfile);
}
}
this.p();
let executableName = project.getSafeName();
if (project.getExecutableName()) {
executableName = project.getExecutableName();
}
let outputname = this.outputExtension === '.html' ? 'index.html' : executableName + this.outputExtension;
this.p('build ' + outputname + ': link ' + ofilelist);
this.closeFile();
}
}

View File

@ -0,0 +1,171 @@
import { Exporter } from 'kmake/Exporters/Exporter';
import { Project } from 'kmake/Project';
import { Platform } from 'kmake/Platform';
import * as fs from 'kmake/fsextra';
import * as path from 'path';
import { CompilerCommandsExporter } from 'kmake/Exporters/CompileCommandsExporter';
export class VSCodeExporter extends Exporter {
compileCommands: CompilerCommandsExporter;
constructor(options: any) {
super(options);
this.compileCommands = new CompilerCommandsExporter(options);
}
configName(platform: string): string {
if (platform === Platform.Windows) {
return 'Win32';
}
else if (platform === Platform.Linux) {
return 'Linux';
}
else if (platform === Platform.OSX) {
return 'Mac';
}
else {
return 'unknown platform';
}
}
compilerPath(platform: string): string {
if (platform === Platform.Windows) {
return 'C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.27.29110/bin/Hostx64/x64/cl.exe';
}
else if (platform === Platform.Linux) {
return '/usr/bin/gcc';
}
else if (platform === Platform.OSX) {
return '/usr/bin/clang';
}
else {
return 'unknown platform';
}
}
async exportSolution(project: Project, from: string, to: string, platform: string, vrApi: any, options: any) {
fs.ensureDirSync(path.join(from, '.vscode'));
this.writeFile(path.join(from, '.vscode', 'c_cpp_properties.json'));
const defines: String[] = [];
for (const define of project.getDefines()) {
defines.push(define.value);
}
const includes: String[] = [];
for (const include of project.getIncludeDirs()) {
if (path.isAbsolute(include)) {
includes.push(include );
}
else {
includes.push('${workspaceFolder}/' + include);
}
}
const config: any = {
name: this.configName(platform),
includePath: includes,
defines: defines,
compilerPath: this.compilerPath(platform),
cStandard: project.cStd,
cppStandard: project.cppStd,
intelliSenseMode: '${default}'
};
if (platform === Platform.Windows) {
config.windowsSdkVersion = '10.0.19041.0';
}
if (platform === Platform.OSX) {
config.macFrameworkPath = ['/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks'];
}
const data = {
configurations: [
config
]
};
this.p(JSON.stringify(data, null, '\t'));
this.closeFile();
this.writeLaunchJson(project, from, to, platform, vrApi, options);
}
writeLaunchJson(project: Project, from: string, to: string, platform: string, vrApi: any, options: any) {
const launchJsonPath = path.join(from, '.vscode', 'launch.json');
let data: any = {
configurations: [],
compounds: []
};
if (fs.existsSync(launchJsonPath)) {
data = JSON.parse(fs.readFileSync(launchJsonPath, {encoding: 'utf8'}));
}
const configurations = [];
for (let config of data.configurations) {
if (!config.name.startsWith('Kore:')) {
configurations.push(config);
}
}
this.writeFile(launchJsonPath);
const koreConfig: any = {
name: 'Kore: Launch',
type: platform === Platform.Windows ? 'cppvsdbg' : 'cppdbg',
request: 'launch',
program: this.program(project, platform),
cwd: project.getDebugDir(),
preLaunchTask: 'Kore: Debug Build for ' + this.preLaunchTask(platform)
};
if (platform === Platform.Windows) {
// koreConfig.symbolSearchPath = 'C:\\Symbols;C:\\SymbolDir2';
koreConfig.externalConsole = true;
koreConfig.logging = {
moduleLoad: false,
trace: true
};
// koreConfig.visualizerFile = '${workspaceFolder}/my.natvis';
}
else if (platform === Platform.OSX) {
koreConfig.MIMode = 'lldb';
}
configurations.push(koreConfig);
data.configurations = configurations;
this.p(JSON.stringify(data, null, '\t'));
this.closeFile();
this.compileCommands.exportSolution(project, from, to, platform, vrApi, options);
}
program(project: Project, platform: string) {
if (platform === Platform.OSX) {
return path.join(project.getBasedir(), 'build', 'build', 'Release', project.getSafeName() + '.app', 'Contents', 'MacOS', project.getSafeName());
}
else {
return path.join(project.getDebugDir(), project.getSafeName() + (platform === Platform.Windows ? '.exe' : ''));
}
}
preLaunchTask(platform: string) {
if (platform === Platform.Windows) {
return 'Windows';
}
else if (platform === Platform.OSX) {
return 'macOS';
}
else if (platform === Platform.Linux) {
return 'Linux';
}
else if (platform === Platform.FreeBSD) {
return 'FreeBSD';
}
else {
return 'Unknown';
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
import { Exporter } from 'kmake/Exporters/Exporter';
import { Project } from 'kmake/Project';
import { CompilerCommandsExporter } from 'kmake/Exporters/CompileCommandsExporter';
import { MakeExporter } from 'kmake/Exporters/MakeExporter';
import { NinjaExporter } from 'kmake/Exporters/NinjaExporter';
import * as Icon from 'kmake/Icon';
import * as path from 'path';
export class WasmExporter extends Exporter {
compileCommands: CompilerCommandsExporter;
make: MakeExporter;
ninja: NinjaExporter;
constructor(options: any) {
super(options);
this.compileCommands = new CompilerCommandsExporter(options);
const compiler = 'clang';
const compilerFlags = '--target=wasm32 -nostdlib -matomics -mbulk-memory';
this.make = new MakeExporter(options, compiler, compiler, compilerFlags, compilerFlags, '--target=wasm32 -nostdlib -matomics -mbulk-memory "-Wl,--import-memory,--shared-memory"', '.wasm');
this.ninja = new NinjaExporter(options, compiler, compiler, compilerFlags, compilerFlags, '--target=wasm32 -nostdlib -matomics -mbulk-memory "-Wl,--import-memory,--shared-memory"', '.wasm');
}
async exportSolution(project: Project, from: string, to: string, platform: string, vrApi: any, options: any) {
this.make.exportSolution(project, from, to, platform, vrApi, options);
this.ninja.exportSolution(project, from, to, platform, vrApi, options);
this.compileCommands.exportSolution(project, from, to, platform, vrApi, options);
await Icon.exportIco(project.icon, path.resolve(to, options.buildPath, 'favicon.ico'), from, true);
}
}

File diff suppressed because it is too large Load Diff

11
kmake/src/GraphicsApi.ts Normal file
View File

@ -0,0 +1,11 @@
export let GraphicsApi = {
Default: 'default',
OpenGL: 'opengl',
OpenGL1: 'opengl1',
Direct3D9: 'direct3d9',
Direct3D11: 'direct3d11',
Direct3D12: 'direct3d12',
Metal: 'metal',
Vulkan: 'vulkan',
WebGPU: 'webgpu'
};

69
kmake/src/Icon.ts Normal file
View File

@ -0,0 +1,69 @@
import * as log from 'kmake/log';
import * as exec from 'kmake/exec';
import * as cp from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
function run(from: string, to: string, width: number, height: number, format: string, background: number, small: boolean, callback: any) {
const exe = path.resolve(__dirname, 'kraffiti' + exec.sys());
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) params.push('background=' + background.toString(16));
if (small) params.push('small');
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) {
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(__dirname, 'icon.png');
}
export async function exportIco(icon: string, to: string, from: string, small: boolean) {
return new Promise(resolve => {
run(findIcon(icon, from.toString()), to.toString(), 0, 0, 'ico', undefined, small, resolve);
});
}
export async function exportIcns(icon: string, to: string, from: string) {
return new Promise(resolve => {
run(findIcon(icon, from.toString()), to.toString(), 0, 0, 'icns', undefined, false, resolve);
});
}
export async function exportPng(icon: string, to: string, width: number, height: number, background: number, from: string) {
return new Promise(resolve => {
run(findIcon(icon, from.toString()), to.toString(), width, height, 'png', background, false, resolve);
});
}
export async function exportPng24(icon: string, to: string, width: number, height: number, background: number, from: string) {
return new Promise(resolve => {
run(findIcon(icon, from.toString()), to.toString(), width, height, 'png24', background, false, resolve);
});
}
export async function exportBmp(icon: string, to: string, width: number, height: number, background: number, from: string) {
return new Promise(resolve => {
run(findIcon(icon, from.toString()), to.toString(), width, height, 'bmp', background, false, resolve);
});
}

7
kmake/src/Languages.ts Normal file
View File

@ -0,0 +1,7 @@
export interface LanguageType {
Beef: string;
}
export let Languages: LanguageType = {
Beef: 'beef',
};

View File

@ -0,0 +1,121 @@
// import * as idl from 'webidl2'; // TODO
import { Language } from './Language';
//import * as path from 'path';
export class BeefLang extends Language {
constructor() {
super();
}
/*async exportWrapper(tree: idl.IDLRootType[], from: string, to: string, options: any, filename: string): Promise<void> {
let p = filename.split('/');
filename = p[p.length - 1].replace('.idl', '.bf');
console.log(filename);
this.writeFile(path.resolve(to, filename));
this.p('using System;');
this.p('using System.Interop;');
this.p('using System.FFI;');
this.p('\n');
this.p('namespace ' + filename.replace('.bf', '') + '_beef\n{');
let indent = 1;
for (let node of tree) {
let type = node.type.toString();
switch (type) {
case 'enum':
let n = <idl.EnumType> node;
this.p('public enum ' + n.name + ' : int32 {', indent);
indent = 2;
for (let val of n.values) {
this.p(val.value + ',', indent);
}
indent = 1;
this.p('}', indent);
break;
case 'interface':
let struct = <idl.InterfaceType> node;
let ext = struct.inheritance !== null ? ' : ' + struct.inheritance : '';
let structType = 'struct';
for (let mem of struct.members) {
if (mem.type === 'operation') {
let opr = <idl.OperationMemberType> mem;
if (opr.name === struct.name) {
structType = 'class';
break;
}
}
}
if (structType === 'struct')
this.p('[CRepr]', indent);
this.p(structType + ' ' + struct.name + ext + '\n {', indent);
indent = 2;
for (let member of struct.members) {
if (member.type === 'attribute') {
let attr = <idl.AttributeMemberType> member;
let isRef = '';
for (let extr of attr.extAttrs) {
if (extr.name === 'Ref') {isRef = '*'; continue; }
}
let type = this.toLangType(attr.idlType.idlType.toString()) + isRef;
let first = structType === 'struct' ? '' : 'public static extern ';
this.p(first + type + ' ' + attr.name + ';', indent);
}
else if (member.type === 'operation') {
let opr = <idl.OperationMemberType> member;
// Can we even interop operator overloads ?
// for (let extAtr of opr.extAttrs) {
// let op = extAtr.rhs !== null ? extAtr.rhs.value : '';
// console.log(extAtr.name+''+op);
// }
let line = 'public static extern ' + this.toLangType(opr.idlType.idlType.toString()) + ' ' + opr.name + '(';
for (let arg of opr.arguments) {
let isRef = '';
for (let attr of arg.extAttrs) {
if (attr.name === 'Ref') {isRef = '*'; continue; }
line += ' ' + attr.name.toLowerCase();
}
line += ' ' + this.toLangType(arg.idlType.idlType.toString()) + isRef + ' ' + arg.name + ', ';
}
if (opr.arguments.length > 0)
line = line.slice(0, -1).slice(0, -1);
line += ');';
this.p(line, indent);
}
}
indent = 1;
this.p('}', indent);
}
if (type === 'eof') {
break;
}
}
this.p('}');
}*/
toLangType(idlType: string): string {
switch (idlType) {
case 'boolean':
return 'bool';
case 'byte':
return 'char';
case 'DOMString':
return 'char8*';
case 'octet':
return 'uint8';
case 'VoidPtr' || 'any':
return 'void*';
case 'long' || 'int':
return 'c_long';
case 'unsigned short':
return 'c_ushort';
case 'unsigned long':
return 'c_ulong';
case 'long long':
return 'c_longlong';
case 'void' || 'double' || 'float':
return idlType;
default:
return idlType;
}
return '';
}
}

View File

@ -0,0 +1,34 @@
import * as fs from '../fsextra';
//import * as path from 'path';
// import * as idl from 'webidl2'; // TODO
export abstract class Language {
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);
}
/*async exportWrapper(tree: idl.IDLRootType[], from: string, to: string, options: any, filename: string): Promise<void> {
return new Promise<void>((resolve, reject) => {
reject('Called an abstract function');
});
}*/
abstract toLangType(idlType: string): string;
}

27
kmake/src/Options.ts Normal file
View File

@ -0,0 +1,27 @@
import { GraphicsApi } from 'kmake/GraphicsApi';
import { Architecture } from 'kmake/Architecture';
import { AudioApi } from 'kmake/AudioApi';
import { VrApi } from 'kmake/VrApi';
import { VisualStudioVersion } from 'kmake/VisualStudioVersion';
import { Compiler } from 'kmake/Compiler';
export let Options = {
precompiledHeaders: false,
intermediateDrive: '',
graphicsApi: GraphicsApi.Default,
architecture: Architecture.Default,
audioApi: AudioApi.Default,
vrApi: VrApi.None,
compiler: Compiler.Default,
ccPath: '',
cxxPath: '',
arPath: '',
stripPath: '',
visualStudioVersion: VisualStudioVersion.VS2022,
followSymbolicLinks: true,
compile: false,
run: false,
cores: 1,
debug: false,
outputIntermediateSpirv: false
};

41
kmake/src/Platform.ts Normal file
View File

@ -0,0 +1,41 @@
export interface PlatformType {
Windows: string;
WindowsApp: string;
iOS: string;
OSX: string;
Android: string;
Linux: string;
Emscripten: string;
Pi: string;
tvOS: string;
PS4: string;
XboxOne: string;
Switch: string;
Switch2: string;
XboxSeries: string;
PS5: string;
FreeBSD: string;
Wasm: string;
Kompjuta: string;
}
export let Platform: PlatformType = {
Windows: 'windows',
WindowsApp: 'windowsapp',
iOS: 'ios',
OSX: 'osx',
Android: 'android',
Linux: 'linux',
Emscripten: 'emscripten',
Pi: 'pi',
tvOS: 'tvos',
PS4: 'ps4',
XboxOne: 'xboxone',
Switch: 'switch',
Switch2: 'switch2',
XboxSeries: 'xboxseries',
PS5: 'ps5',
FreeBSD: 'freebsd',
Wasm: 'wasm',
Kompjuta: 'kompjuta',
};

969
kmake/src/Project.ts Normal file
View File

@ -0,0 +1,969 @@
import * as path from 'path';
import * as fs from 'kmake/fsextra';
import * as log from 'kmake/log';
import { GraphicsApi } from 'kmake/GraphicsApi';
import { Architecture } from 'kmake/Architecture';
import { AudioApi } from 'kmake/AudioApi';
import { VrApi } from 'kmake/VrApi';
import { Options } from 'kmake/Options';
import { Platform } from 'kmake/Platform';
import * as crypto from 'crypto';
function getDefines(platform: string, rotated: boolean) {
let defines: string[] = [];
switch (platform) {
case Platform.iOS:
if (rotated) defines.push('ROTATE90');
break;
case Platform.Android:
if (rotated) defines.push('ROTATE90');
break;
}
return defines;
}
function contains(array: any[], value: any) {
for (const element of array) {
if (element === value) {
return true;
}
}
return false;
}
function containsDefine(array: Define[], value: Define) {
for (const element of array) {
if (element.value === value.value && element.config === value.config) {
return true;
}
}
return false;
}
function containsFancyDefine(array: Define[], value: Define) {
const name = value.value.substring(0, value.value.indexOf('='));
for (const element of array) {
if (element.config === value.config) {
const index = element.value.indexOf('=');
if (index >= 0) {
const otherName = element.value.substring(0, index);
if (name === otherName) {
return true;
}
}
}
}
return false;
}
function isAbsolute(path: string) {
return (path.length > 0 && path[0] === '/') || (path.length > 1 && path[1] === ':');
}
let projectInProgress = 0;
process.on('exit', (code: number) => {
if (projectInProgress > 0) {
process.exitCode = 1;
log.error('Error: kfile did not call resolve, no project created.');
}
});
let scriptdir = '.';
// let lastScriptDir = '.';
let cppEnabled = false;
function findKore() {
return Project.koreDir;
}
async function loadProject(directory: string, parent: Project, options: any = {}, korefile: string = null): Promise<Project> {
return new Promise<Project>((resolve, reject) => {
projectInProgress += 1;
let resolver = async (project: Project) => {
projectInProgress -= 1;
// TODO: This accidentally finds Kha/Backends/KoreHL
/*if (fs.existsSync(path.join(scriptdir, 'Backends'))) {
var libdirs = fs.readdirSync(path.join(scriptdir, 'Backends'));
for (var ld in libdirs) {
var libdir = path.join(scriptdir, 'Backends', libdirs[ld]);
if (fs.statSync(libdir).isDirectory()) {
var korefile = path.join(libdir, korefile);
if (fs.existsSync(korefile)) {
project.addSubProject(await Project.createProject(libdir, scriptdir));
}
}
}
}*/
resolve(project);
};
try {
scriptdir = directory;
if (!korefile) {
if (fs.existsSync(path.resolve(directory, 'kfile.js'))) {
korefile = 'kfile.js';
}
else if (fs.existsSync(path.resolve(directory, 'kincfile.js'))) {
korefile = 'kincfile.js';
}
else if (fs.existsSync(path.resolve(directory, 'korefile.js'))) {
korefile = 'korefile.js';
cppEnabled = true;
}
}
let file = fs.readFileSync(path.resolve(directory, korefile), 'utf8');
let AsyncFunction = Object.getPrototypeOf(async () => {}).constructor;
Project.currentParent = parent;
let project = new AsyncFunction(
'log',
'Project',
'Platform',
'platform',
'GraphicsApi',
'graphics',
'Architecture',
'arch',
'AudioApi',
'audio',
'VrApi',
'vr',
'cpp',
'require',
'resolve',
'reject',
'__dirname',
'Options',
'targetDirectory',
'parentProject',
'findKore',
'findKinc',
file)
(
log,
Project,
Platform,
Project.platform,
GraphicsApi,
Options.graphicsApi,
Architecture,
Options.architecture,
AudioApi,
Options.audioApi,
VrApi,
Options.vrApi,
cppEnabled,
require,
resolver,
reject,
directory,
options,
Project.to,
parent,
findKore,
findKore).catch(
(error: any) => {
log.error(error);
reject();
}
);
}
catch (error) {
log.error(error);
reject();
}
});
}
export interface File {
file: string;
options: any;
projectDir: string;
projectName: string;
}
export class Define {
value: string;
config: string;
}
export class Project {
static platform: string;
static koreDir: string;
static root: string;
static to: string;
name: string;
safeName: string;
version: string;
id: string;
debugDir: string;
basedir: string;
uuid: string;
files: File[];
IDLfiles: string[];
javadirs: string[];
subProjects: Project[];
includeDirs: string[];
defines: Define[];
systemDependendDefines: any;
libs: string[];
systemDependendLibraries: any;
includes: {file: string, options: any}[];
excludes: string[];
customs: {file: string, command: string, output: string}[];
cppStd: string = '';
cStd: string = '';
kore: boolean;
koreProcessed: boolean;
targetOptions: any;
rotated: boolean;
cmd: boolean;
cmdArgs: string[] = [];
cFlags: string[] = [];
cppFlags: string[] = [];
linkerFlags: string[] = [];
stackSize: number;
icon: string = null;
livePP: string = null;
additionalBackends: string[] = [];
vsdeploy: boolean = false;
linkTimeOptimization: boolean = true;
macOSnoArm: boolean = false;
noFlatten: boolean = true;
isStaticLib: boolean = false;
isDynamicLib: boolean = false;
static currentParent: Project = null;
parent: Project;
shaderVersion: number;
kongDirs: string[];
kope: boolean = false;
executableName: string;
createHash(vscode: boolean, json: boolean, debug: boolean, platform: string): string {
const sha = crypto.createHash('sha1');
const uuid = this.uuid;
this.uuid = '';
sha.update(JSON.stringify(this));
this.uuid = uuid;
sha.update(vscode ? 'true' : 'false');
sha.update(json ? 'true' : 'false');
sha.update(debug ? 'true' : 'false');
sha.update(platform);
return sha.digest('hex');
}
constructor(name: string) {
this.name = name;
this.parent = Project.currentParent;
this.safeName = name.replace(/[^A-z0-9\-\_]/g, '-');
this.version = '1.0';
this.debugDir = '';
this.basedir = scriptdir;
this.uuid = crypto.randomUUID();
this.files = [];
this.IDLfiles = [];
this.customs = [];
this.javadirs = [];
this.subProjects = [];
this.includeDirs = [];
this.defines = [];
this.libs = [];
this.kongDirs = [];
this.systemDependendLibraries = {};
this.includes = [];
this.excludes = [];
this.cppStd = '';
this.cStd = '';
this.kore = true;
this.targetOptions = {
android: {},
xboxOne: {},
playStation4: {},
switch: {},
switch2: {},
xboxSeriesXS: {},
playStation5: {},
stadia: {},
emscripten: {}
};
this.rotated = false;
this.cmd = false;
this.stackSize = 0;
this.koreProcessed = false;
this.executableName = null;
}
setExecutableName(name: string) {
this.executableName = name;
}
getExecutableName(): string {
return this.executableName;
}
addBackend(name: string) {
this.additionalBackends.push(name);
}
findAdditionalBackends(additionalBackends: string[]) {
for (const backend of this.additionalBackends) {
additionalBackends.push(backend);
}
for (let sub of this.subProjects) {
sub.findAdditionalBackends(additionalBackends);
}
}
findKoreProject(): Project {
if (this.name === 'Kore') {
return this;
}
if (this.name === 'Kinc') {
return this;
}
for (let sub of this.subProjects) {
let kore = sub.findKoreProject();
if (kore != null) {
return kore;
}
}
return null;
}
resolveBackends() {
let additionalBackends: string[] = [];
this.findAdditionalBackends(additionalBackends);
let kore = this.findKoreProject();
for (const backend of additionalBackends) {
kore.addFile('Backends/' + backend + '/Sources/k3/**', null);
kore.addFile('Backends/' + backend + '/Sources/kinc/**', null);
kore.addFile('Backends/' + backend + '/Sources/GL/**', null);
kore.addFile('Backends/' + backend + '/Sources/Android/**', null);
//if (Options.kope) {
// kore.addFile('Backends/' + backend + '/Sources/kope/**', {nocompile: true});
// kore.addFile('Backends/' + backend + '/Sources/kope/**/*unit.c*', null);
//}
kore.addIncludeDir('Backends/' + backend + '/Sources');
}
}
flattenSubProjects() {
for (let sub of this.subProjects) {
sub.noFlatten = false;
sub.flattenSubProjects();
}
}
flatten() {
this.noFlatten = false;
this.flattenSubProjects();
}
internalFlatten() {
let out = [];
for (let sub of this.subProjects) sub.internalFlatten();
for (let sub of this.subProjects) {
if (sub.noFlatten) {
out.push(sub);
}
else {
if (sub.kope) {
this.kope = true;
}
if (sub.cppStd !== '') {
this.cppStd = sub.cppStd;
}
if (sub.cStd !== '') {
this.cStd = sub.cStd;
}
if (sub.cmd) {
this.cmd = true;
}
if (sub.vsdeploy) {
this.vsdeploy = true;
}
if (!sub.linkTimeOptimization) {
this.linkTimeOptimization = false;
}
if (sub.macOSnoArm) {
this.macOSnoArm = true;
}
if (this.shaderVersion) {
if (sub.shaderVersion && sub.shaderVersion > this.shaderVersion) {
this.shaderVersion = sub.shaderVersion;
}
}
else if (sub.shaderVersion) {
this.shaderVersion = sub.shaderVersion;
}
if (sub.livePP) {
this.livePP = this.livePP;
}
let subbasedir = sub.basedir;
for (let tkey of Object.keys(sub.targetOptions)) {
const target = sub.targetOptions[tkey];
for (let key of Object.keys(target)) {
const options = this.targetOptions[tkey];
const option = target[key];
if (options[key] == null) options[key] = option;
// push library properties to current array instead
else if (Array.isArray(options[key]) && Array.isArray(option)) {
for (let value of option) {
if (!options[key].includes(value)) options[key].push(value);
}
}
}
}
for (let d of sub.defines) {
if (d.value.indexOf('=') >= 0) {
if (!containsFancyDefine(this.defines, d)) {
this.defines.push(d);
}
}
else {
if (!containsDefine(this.defines, d)) {
this.defines.push(d);
}
}
}
for (let file of sub.files) {
let absolute = file.file;
if (!path.isAbsolute(absolute)) {
absolute = path.join(subbasedir, file.file);
}
let subFileExist = false;
let subFile = absolute.replace(/\\/g, '/');
for (let mainFile of this.files) {
subFileExist = mainFile.file == subFile;
if (subFileExist) {
break;
}
}
if (subFileExist) {
continue;
}
this.files.push({file: subFile, options: file.options, projectDir: subbasedir, projectName: sub.name });
}
for (const custom of sub.customs) {
let absolute = custom.file;
if (!path.isAbsolute(absolute)) {
absolute = path.join(subbasedir, custom.file);
}
this.customs.push({file: absolute.replace(/\\/g, '/'), command: custom.command, output: custom.output });
}
for (let i of sub.includeDirs) if (!contains(this.includeDirs, path.resolve(subbasedir, i))) this.includeDirs.push(path.resolve(subbasedir, i));
for (let j of sub.javadirs) if (!contains(this.javadirs, path.resolve(subbasedir, j))) this.javadirs.push(path.resolve(subbasedir, j));
for (let k of sub.kongDirs) if (!contains(this.kongDirs, path.resolve(subbasedir, k))) this.kongDirs.push(path.resolve(subbasedir, k));
for (let lib of sub.libs) {
if (lib.indexOf('/') < 0 && lib.indexOf('\\') < 0) {
if (!contains(this.libs, lib)) this.libs.push(lib);
}
else {
if (!contains(this.libs, path.resolve(subbasedir, lib))) this.libs.push(path.resolve(subbasedir, lib));
}
}
for (let system in sub.systemDependendLibraries) {
let libs = sub.systemDependendLibraries[system];
for (let lib of libs) {
if (this.systemDependendLibraries[system] === undefined) this.systemDependendLibraries[system] = [];
if (!contains(this.systemDependendLibraries[system], this.stringify(path.resolve(subbasedir, lib)))) {
if (!contains(lib, '/') && !contains(lib, '\\')) this.systemDependendLibraries[system].push(lib);
else this.systemDependendLibraries[system].push(this.stringify(path.resolve(subbasedir, lib)));
}
}
}
for (let flag of sub.cFlags) {
if (!this.cFlags.includes(flag)) {
this.cFlags.push(flag);
}
}
for (let flag of sub.cppFlags) {
if (!this.cppFlags.includes(flag)) {
this.cppFlags.push(flag);
}
}
for (let flag of sub.linkerFlags) {
if (!this.linkerFlags.includes(flag)) {
this.linkerFlags.push(flag);
}
}
}
}
this.subProjects = out;
}
getName() {
return this.name;
}
getSafeName() {
return this.safeName;
}
getUuid() {
return this.uuid;
}
matches(text: string, pattern: string) {
const regexstring = pattern.replace(/\./g, '\\.').replace(/\*\*/g, '.?').replace(/\*/g, '[^/]*').replace(/\?/g, '*');
const regex = new RegExp('^' + regexstring + '$', 'g');
return regex.test(text);
}
matchesAllSubdirs(dir: string, pattern: string) {
if (pattern.endsWith('/**')) {
return this.matches(this.stringify(dir), pattern.substr(0, pattern.length - 3));
}
else return false;
}
stringify(path: string) {
return path.replace(/\\/g, '/');
}
addCFlag(flag: string) {
this.cFlags.push(flag);
}
addCFlags() {
for (let i = 0; i < arguments.length; ++i) {
if (typeof arguments[i] === 'string') {
this.addCFlag(arguments[i]);
}
}
}
addCppFlag(flag: string) {
this.cppFlags.push(flag);
}
addCppFlags() {
for (let i = 0; i < arguments.length; ++i) {
if (typeof arguments[i] === 'string') {
this.addCppFlag(arguments[i]);
}
}
}
addLinkerFlag(flag: string) {
this.linkerFlags.push(flag);
}
addLinkerFlags() {
for (let i = 0; i < arguments.length; ++i) {
if (typeof arguments[i] === 'string') {
this.addLinkerFlag(arguments[i]);
}
}
}
addFileForReal(file: string, options: any) {
for (let index in this.files) {
if (this.files[index].file === file) {
this.files[index] = {file: file, options: options, projectDir: this.basedir, projectName: this.name};
return;
}
}
this.files.push({file: file, options: options, projectDir: this.basedir, projectName: this.name});
}
searchFiles(current: any) {
if (current === undefined) {
for (let sub of this.subProjects) sub.searchFiles(undefined);
this.searchFiles(this.basedir);
for (let includeobject of this.includes) {
if (path.isAbsolute(includeobject.file) && includeobject.file.includes('**')) {
const starIndex = includeobject.file.indexOf('**');
const endIndex = includeobject.file.substring(0, starIndex).replace(/\\/g, '/').lastIndexOf('/');
this.searchFiles(includeobject.file.substring(0, endIndex));
}
if (includeobject.file.startsWith('../')) {
let startNext = '../';
let start = '../';
while (includeobject.file.startsWith(startNext)) {
start = startNext;
startNext += '../';
}
this.searchFiles(path.resolve(this.basedir, start));
}
}
// std::set<std::string> starts;
// for (std::string include : includes) {
// if (!isAbsolute(include)) continue;
// std::string start = include.substr(0, firstIndexOf(include, '*'));
// if (starts.count(start) > 0) continue;
// starts.insert(start);
// searchFiles(Paths::get(start));
// }
return;
}
let files = fs.readdirSync(current);
nextfile: for (let f in files) {
let file = path.join(current, files[f]);
let follow = true;
try {
if (fs.statSync(file).isDirectory()) {
follow = false;
}
}
catch (err) {
follow = false;
}
if (!follow) {
continue;
}
// if (!current.isAbsolute())
file = path.relative(this.basedir, file);
for (let exclude of this.excludes) {
if (this.matches(this.stringify(file), exclude)) {
continue nextfile;
}
}
for (let includeobject of this.includes) {
let include = includeobject.file;
if (isAbsolute(include)) {
let inc = include;
inc = path.relative(this.basedir, inc);
include = inc;
}
if (this.matches(this.stringify(file), this.stringify(include))) {
this.addFileForReal(this.stringify(file), includeobject.options);
}
}
}
let dirs = fs.readdirSync(current);
nextdir: for (let d of dirs) {
let dir = path.join(current, d);
if (d.startsWith('.')) continue;
let follow = true;
try {
const stats = fs.statSync(dir);
if (!stats.isDirectory()) {
follow = false;
}
if (!Options.followSymbolicLinks && stats.isSymbolicLink()) {
follow = false;
}
}
catch (err) {
follow = false;
}
if (!follow) {
continue;
}
for (let exclude of this.excludes) {
if (this.matchesAllSubdirs(path.relative(this.basedir, dir), exclude)) {
continue nextdir;
}
}
this.searchFiles(dir);
}
}
addFile(file: string, options: any) {
this.includes.push({file: file, options: options});
}
addCustomFile(file: string, command: string, output: string) {
this.customs.push({file, command, output});
}
addFiles() {
let options: any = undefined;
for (let i = 0; i < arguments.length; ++i) {
if (typeof arguments[i] !== 'string') {
options = arguments[i];
}
}
for (let i = 0; i < arguments.length; ++i) {
if (typeof arguments[i] === 'string') {
this.addFile(arguments[i], options);
}
}
}
addIdlDef() {
for (let i = 0; i < arguments.length; ++i) {
if (typeof arguments[i] === 'string') {
this.IDLfiles.push(arguments[i]);
}
}
}
addJavaDir(dir: string) {
this.javadirs.push(dir);
}
addJavaDirs() {
for (let i = 0; i < arguments.length; ++i) {
this.addJavaDir(arguments[i]);
}
}
addExclude(exclude: string) {
this.excludes.push(exclude);
}
addExcludes() {
for (let i = 0; i < arguments.length; ++i) {
this.addExclude(arguments[i]);
}
}
addDefine(value: string, config: string = null) {
const define = {value, config};
if (containsDefine(this.defines, define)) {
return;
}
this.defines.push(define);
}
addDefines() {
for (let i = 0; i < arguments.length; ++i) {
this.addDefine(arguments[i]);
}
}
addDefineFor(system: string, define: string) {
if (this.systemDependendDefines[system] === undefined) this.systemDependendDefines[system] = [];
this.systemDependendDefines[system].push(define);
}
addDefinesFor() {
if (this.systemDependendDefines[arguments[0]] === undefined) this.systemDependendDefines[arguments[0]] = [];
for (let i = 1; i < arguments.length; ++i) {
this.systemDependendDefines[arguments[0]].push(arguments[i]);
}
}
removeDefine(value: string, config: string = null) {
this.defines = this.defines.filter((element: Define) => {
if (element.value === value && element.config === config) {
return false;
}
return true;
});
}
addIncludeDir(include: string) {
if (contains(this.includeDirs, include)) return;
this.includeDirs.push(include);
}
addIncludeDirs() {
for (let i = 0; i < arguments.length; ++i) {
this.addIncludeDir(arguments[i]);
}
}
addLib(lib: string) {
this.libs.push(lib);
}
addLibs() {
for (let i = 0; i < arguments.length; ++i) {
this.addLib(arguments[i]);
}
}
addLibFor(system: string, lib: string) {
if (this.systemDependendLibraries[system] === undefined) this.systemDependendLibraries[system] = [];
this.systemDependendLibraries[system].push(lib);
}
addLibsFor() {
if (this.systemDependendLibraries[arguments[0]] === undefined) this.systemDependendLibraries[arguments[0]] = [];
for (let i = 1; i < arguments.length; ++i) {
this.systemDependendLibraries[arguments[0]].push(arguments[i]);
}
}
addKongDir(dir: string) {
this.kongDirs.push(dir);
this.addDefine('KORE_KONG');
}
addKongDirs() {
for (let i = 0; i < arguments.length; ++i) {
this.addKongDir(arguments[i]);
}
}
getFiles() {
return this.files;
}
getJavaDirs() {
return this.javadirs;
}
getKongDirs() {
return this.kongDirs;
}
getBasedir() {
return this.basedir;
}
getSubProjects() {
return this.subProjects;
}
getIncludeDirs() {
return this.includeDirs;
}
getDefines() {
return this.defines;
}
getLibs() {
return this.libs;
}
getLibsFor(system: string) {
if (this.systemDependendLibraries[system] === undefined) return [];
return this.systemDependendLibraries[system];
}
getDebugDir() {
return this.debugDir;
}
setDebugDir(debugDir: string) {
this.debugDir = path.resolve(this.basedir, debugDir);
if(!fs.existsSync(this.debugDir)) {
throw new Error(`Debug directory ${this.debugDir} does not exist`);
}
}
getCppStd() {
return this.cppStd;
}
setCppStd(std: string) {
this.cppStd = std;
}
getCStd() {
return this.cStd;
}
setCStd(std: string) {
this.cStd = std;
}
getParentProjet(): Project {
return this.parent;
}
getRootProject(): Project {
if (!this.parent) {
return this;
}
return this.parent.getRootProject();
}
addLivePP(livePP: string) {
this.livePP = livePP;
}
async addProject(directory: string, options: any = {}, projectFile: string = null) {
let from = path.isAbsolute(directory) ? directory : path.join(this.basedir, directory);
if (fs.existsSync(from) && fs.statSync(from).isDirectory()) {
const project = await loadProject(from, this, options, projectFile);
if (options.kope) {
project.kope = true;
}
this.subProjects.push(project);
return project;
}
else {
throw 'Project directory ' + from + ' not found';
}
}
static async create(directory: string, to: string, platform: string, korefile: string, retro: boolean, veryretro: boolean, options: any = {}) {
Project.platform = platform;
Project.to = path.resolve(to);
try {
let project = await loadProject(path.resolve(directory), null, options, korefile);
if (retro && project.kore && !project.koreProcessed) {
if (veryretro) {
if (Project.koreDir) {
await project.addProject(Project.koreDir);
}
}
else {
await project.addProject(Project.koreDir);
}
project.flatten();
}
let defines = getDefines(platform, project.isRotated());
for (let define of defines) {
project.addDefine(define);
}
return project;
}
catch (err) {
throw 'Could not create project ' + directory;
}
}
isRotated() {
return this.rotated;
}
isCmd() {
return this.cmd;
}
setRotated() {
this.rotated = true;
}
setCmd() {
this.cmd = true;
}
set cpp(value: boolean) {
cppEnabled = value;
}
setMinimumShaderVersion(version:number) {
this.shaderVersion = version;
}
// deprecated
static createProject(): Promise<void> {
log.info('Warning: createProject was removed, see updates.md for instructions.');
return new Promise<void>((resolve, reject) => {
resolve();
});
}
// deprecated
addSubProject() {
}
}

447
kmake/src/ShaderCompiler.ts Normal file
View File

@ -0,0 +1,447 @@
import * as child_process from 'child_process';
import * as fs from 'kmake/fsextra';
import * as path from 'path';
import {GraphicsApi} from 'kmake/GraphicsApi';
import {Options} from 'kmake/Options';
import {Platform} from 'kmake/Platform';
import * as log from 'kmake/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 {
platform: string;
compiler: string;
type: string;
to: string;
temp: string;
builddir: string;
shaderMatchers: Array<{ match: string, options: any }>;
watcher: fs.FSWatcher;
constructor(platform: string, compiler: string, to: string, temp: string, builddir: string, shaderMatchers: Array<{ match: string, options: any }>) {
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);
this.to = to;
this.temp = temp;
this.builddir = builddir;
this.shaderMatchers = shaderMatchers;
}
close(): void {
if (this.watcher) this.watcher.close();
}
static findType(platform: string): string {
switch (platform) {
case Platform.Android:
if (Options.graphicsApi === GraphicsApi.Vulkan || Options.graphicsApi === GraphicsApi.Default) {
return 'spirv';
}
else if (Options.graphicsApi === GraphicsApi.OpenGL) {
return 'essl';
}
else {
throw new Error('Unsupported shader language.');
}
case Platform.Emscripten:
case Platform.Wasm:
case Platform.Pi:
return 'essl';
case Platform.tvOS:
case Platform.iOS:
if (Options.graphicsApi === GraphicsApi.Metal || Options.graphicsApi === GraphicsApi.Default) {
return 'metal';
}
else if (Options.graphicsApi === GraphicsApi.OpenGL) {
return 'essl';
}
else {
throw new Error('Unsupported shader language.');
}
case Platform.Windows:
if (Options.graphicsApi === GraphicsApi.Vulkan) {
return 'spirv';
}
else if (Options.graphicsApi === GraphicsApi.OpenGL) {
return 'glsl';
}
else if (Options.graphicsApi === GraphicsApi.Direct3D11 || Options.graphicsApi === GraphicsApi.Direct3D12 || Options.graphicsApi === GraphicsApi.Default) {
return 'd3d11';
}
else if (Options.graphicsApi === GraphicsApi.Direct3D9) {
return 'd3d9';
}
else {
throw new Error('Unsupported shader language.');
}
case Platform.WindowsApp:
return 'd3d11';
case Platform.Linux:
if (Options.graphicsApi === GraphicsApi.Vulkan || Options.graphicsApi === GraphicsApi.Default) {
return 'spirv';
}
else if (Options.graphicsApi === GraphicsApi.OpenGL) {
return 'glsl';
}
else {
throw new Error('Unsupported shader language.');
}
case Platform.OSX:
if (Options.graphicsApi === GraphicsApi.Metal || Options.graphicsApi === GraphicsApi.Default) {
return 'metal';
}
else if (Options.graphicsApi === GraphicsApi.OpenGL) {
return 'glsl';
}
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 = fs.watch(match); // 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 = parsed.name; //AssetConverter.createExportInfo(parsed, false, options, Options.from).name;
compiledShaders.push(compiledShader);
}
else {
log.info('Skipping shader ' + (index + 1) + ' of ' + shaders.length + ' (' + parsed.base + ').');
}
++index;
return Promise.resolve();
}
/*if (Options.parallelAssetConversion !== 0) {
let todo = shaders.map((shader, index) => {
return async () => {
await compile(shader, index);
};
});
let processes = 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.copyFileSync(from, to);
}
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') {
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 (Options.shaderversion) {
// parameters.push('--version');
// parameters.push(Options.shaderversion);
// }
if (Options.debug) {
parameters.push('--debug');
}
if (options.defines) {
for (let define of options.defines) {
parameters.push('-D' + define);
}
}
if (this.platform === Platform.Emscripten || 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') {
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.');
}
});
}
});
});
});
});
}
}

View File

@ -0,0 +1,10 @@
export let VisualStudioVersion = {
VS2010: 'vs2010',
VS2012: 'vs2012',
VS2013: 'vs2013',
VS2015: 'vs2015',
VS2017: 'vs2017',
VS2019: 'vs2019',
VS2022: 'vs2022',
VS2026: 'vs2026'
};

8
kmake/src/VrApi.ts Normal file
View File

@ -0,0 +1,8 @@
export let VrApi = {
GearVr: 'gearvr',
Cardboard: 'cardboard',
Oculus: 'oculus',
SteamVR: 'steamvr',
HoloLens: 'hololens',
None: 'none'
};

10
kmake/src/exec.ts Normal file
View File

@ -0,0 +1,10 @@
import * as os from 'os';
export function sys() {
if (os.platform() === 'win32') {
return '.exe';
}
else {
return '';
}
}

65
kmake/src/fsextra.ts Normal file
View File

@ -0,0 +1,65 @@
import * as log from 'kmake/log';
import {
copyFileSync as fsCopyFileSync,
existsSync as fsExistsSync,
readdirSync as fsReaddirSync,
statSync as fsStatSync,
writeFileSync as fsWriteFileSync,
readFileSync as fsReadFileSync,
mkdirSync as fsMkdirSync,
openSync as fsOpenSync,
writeSync as fsWriteSync,
closeSync as fsCloseSync,
chmodSync as fsChmodSync,
renameSync as fsRenameSync,
stat as fsStat,
Stats as FsStats,
FSWatcher as FsFSWatcher,
watch as fsWatch,
} from 'fs';
import * as path from 'path';
function ensureDirSync(dir: string): void {
try {
if (!fsExistsSync(dir)) {
fsMkdirSync(dir, { recursive: true });
}
}
catch (err) {
log.error(`Error creating directory ${dir}: ${err}`);
}
}
function copyDirSync(from: string, to: string): void {
ensureDirSync(to);
const files = fsReaddirSync(from);
for (const file of files) {
const stat = fsStatSync(path.join(from, file));
if (stat.isDirectory()) {
copyDirSync(path.join(from, file), path.join(to, file));
}
else {
fsCopyFileSync(path.join(from, file), path.join(to, file));
}
}
}
export {
fsExistsSync as existsSync,
fsReaddirSync as readdirSync,
fsStatSync as statSync,
fsWriteFileSync as writeFileSync,
fsReadFileSync as readFileSync,
fsOpenSync as openSync,
fsWriteSync as writeSync,
fsCloseSync as closeSync,
fsCopyFileSync as copyFileSync,
fsChmodSync as chmodSync,
fsRenameSync as renameSync,
fsStat as stat,
FsStats as Stats,
FsFSWatcher as FSWatcher,
fsWatch as watch,
ensureDirSync,
copyDirSync
};

40
kmake/src/init.ts Normal file
View File

@ -0,0 +1,40 @@
import * as fs from 'fs';
import * as path from 'path';
export function run(name: string, from: string) {
const projectfile = 'kfile.js';
if (!fs.existsSync(path.join(from, projectfile))) {
fs.writeFileSync(path.join(from, projectfile),
'let project = new Project(\'New Project\');\n'
+ '\n'
+ 'await project.addProject(\'Kore\');\n'
+ '\n'
+ 'project.addFile(\'Sources/**\');\n'
+ 'project.setDebugDir(\'Deployment\');\n'
+ '\n'
+ 'project.flatten();\n'
+ '\n'
+ 'resolve(project);\n',
{ encoding: 'utf8' });
}
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.c'))) {
let mainsource = '\n'
+ 'int kickstart(int argc, char** argv) {\n'
+ '\treturn 0;\n'
+ '}\n';
fs.writeFileSync(path.join(from, 'Sources', 'main.c'), mainsource, { encoding: 'utf8' });
}
if (!fs.existsSync(path.join(from, 'Deployment'))) {
fs.mkdirSync(path.join(from, 'Deployment'));
}
}

30
kmake/src/log.ts Normal file
View File

@ -0,0 +1,30 @@
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: any) {
myInfo = log.info;
myError = log.error;
}
export function info(text: string, newline: boolean = true) {
myInfo(text, newline);
}
export function error(text: string, newline: boolean = true) {
myError(text, newline);
}

1239
kmake/src/main.ts Normal file

File diff suppressed because it is too large Load Diff

83
kmake/src/server.ts Normal file
View File

@ -0,0 +1,83 @@
import * as fs from 'fs';
import * as http from 'http';
import * as log from 'kmake/log';
import * as path from 'path';
export async function run(options: any, loglog: any): Promise<void> {
log.set(loglog);
log.info('Running server on ' + options.port + '...');
const server = http.createServer((request, response) => {
let baseDir = 'build/release';
if (options.debug) {
baseDir = 'build/debug';
}
let filePath = baseDir + request.url;
if (request.url === '/') {
filePath = baseDir + '/index.html';
}
const extname = path.extname(filePath);
let contentType = 'text/html';
switch (extname) {
case '.js':
contentType = 'text/javascript';
break;
case '.css':
contentType = 'text/css';
break;
case '.json':
contentType = 'application/json';
break;
case '.png':
contentType = 'image/png';
break;
case '.jpg':
contentType = 'image/jpg';
break;
case '.wav':
contentType = 'audio/wav';
break;
case '.wasm':
contentType = 'application/wasm';
break;
}
log.info('Reading file ' + filePath + '.');
fs.readFile(filePath, (error, content) => {
response.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
response.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
if (error) {
if (error.code == 'ENOENT'){
log.info('File ' + filePath + ' not found.');
fs.readFile('./404.html', (error, content) => {
response.writeHead(200, { 'Content-Type': contentType });
response.end(content, 'utf-8');
});
}
else {
response.writeHead(500);
response.end('Error: ' + error.code + ' ..\n');
response.end();
}
}
else {
response.writeHead(200, { 'Content-Type': contentType });
response.end(content, 'utf-8');
}
});
});
server.on('error', (e) => {
if (e.name === 'EADDRINUSE') {
log.error('Error: Port ' + options.port + ' is already in use.');
log.error('Please close the competing program (maybe another instance of kmake?)');
log.error('or switch to a different port using the --port argument.');
}
});
server.listen(options.port);
}