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,21 @@
cmake_minimum_required(VERSION 3.22.1)
project("Kore")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}{debug_defines}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}{debug_defines}")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}{release_defines}")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}{release_defines}")
include_directories(
{includes})
add_library(
kore
SHARED
{files})
{libraries1}
target_link_libraries(
kore
{libraries2})

View File

@ -0,0 +1,59 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "{package}"
compileSdk = {compileSdkVersion}
defaultConfig {
applicationId = "{package}"
minSdk = {minSdkVersion}
targetSdk = {targetSdkVersion}
versionCode = {versionCode}
versionName = "{versionName}"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
sourceSets.getByName("main") {
java.setSrcDirs(listOf({javasources}))
}
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
{architecture}
}
debug {
isMinifyEnabled = false
{architecture}
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
externalNativeBuild {
cmake {
path = file("CMakeLists.txt")
version = "3.22.1"
}
}
buildFeatures {
viewBinding = true
}
}
dependencies {
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.8.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}

View File

@ -0,0 +1 @@
/build

View File

@ -0,0 +1,34 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keep public class tech.kore.KoreActivity {
public <methods>;
}
-keep public class tech.kore.KoreMoviePlayer {
public <methods>;
}
-keep public class tech.kore.KoreMovieTexture {
public <methods>;
}
-keepclasseswithmembernames,includedescriptorclasses class * {
native <methods>;
}

View File

@ -0,0 +1,5 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id("com.android.application") version "8.1.0" apply false
id("org.jetbrains.kotlin.android") version "1.8.0" apply false
}

View File

@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

View File

@ -0,0 +1,23 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true

Binary file not shown.

View File

@ -0,0 +1,6 @@
#Fri Aug 04 18:00:20 CEST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

185
kmake/Data/android/gradlew vendored Normal file
View File

@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

89
kmake/Data/android/gradlew.bat vendored Normal file
View File

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>

View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="jbr-17" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.0" />
</component>
</project>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/{name}.app.main.iml" filepath="$PROJECT_DIR$/.idea/modules/app/{name}.app.main.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$/../../../app/src/main">
<sourceFolder url="file://$MODULE_DIR$/../../../" type="native-Source-root" />
</content>
</component>
</module>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:installLocation="{installLocation}">
{permissions}
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
tools:targetApi="{targetSdkVersion}">
<activity
android:name="tech.kore.KoreActivity" android:label="@string/app_name" android:configChanges="orientation|keyboardHidden|keyboard|screenSize"
android:exported="true" android:screenOrientation="{screenOrientation}"
android:launchMode="singleTask"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
<meta-data android:name="android.app.lib_name" android:value="kore" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,3 @@
<resources>
<string name="app_name">{name}</string>
</resources>

View File

@ -0,0 +1,18 @@
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "{name}"
include(":app")

View File

@ -0,0 +1,36 @@
ENTRY(_start)
MEMORY {
RAM (rwx) : ORIGIN = 0x00010000, LENGTH = 256M
}
SECTIONS {
.text : ALIGN(16) {
KEEP(*(.text.start))
*(.text*)
*(.rodata*)
} > RAM
.eh_frame : { *(.eh_frame*) } > RAM
.data : ALIGN(16) {
__data_start = .;
*(.data*)
__data_end = .;
} > RAM
.bss (NOLOAD) : ALIGN(16) {
__bss_start = .;
*(.bss*)
*(.sbss*)
*(COMMON)
__bss_end = .;
} > RAM
. = ALIGN(16);
__heap_base = .;
__heap_end = ORIGIN(RAM) + LENGTH(RAM) - 1M;
__stack_bottom = __heap_end;
__stack_top = __heap_end + 1M;
}

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$">
<contentRoot DIR="{root}" />
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunManager" selected="Application.ShaderTest">
<configuration default="false" name="ShaderTest" type="CMakeRunConfiguration" factoryName="Application" WORKING_DIR="{workingdir}" PASS_PARENT_ENVS_2="true" PROJECT_NAME="{project}" TARGET_NAME="{target}" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="{project}" RUN_TARGET_NAME="{target}">
<envs />
<method />
</configuration>
</component>
</project>

50
kmake/package-lock.json generated Normal file
View File

@ -0,0 +1,50 @@
{
"name": "kmake",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "kmake",
"version": "1.0.0",
"license": "Zlib",
"devDependencies": {
"@types/node": "^18.15.3",
"typescript": "^4.9.5"
}
},
"node_modules/@types/node": {
"version": "18.15.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz",
"integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==",
"dev": true
},
"node_modules/typescript": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
}
},
"dependencies": {
"@types/node": {
"version": "18.15.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz",
"integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==",
"dev": true
},
"typescript": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"dev": true
}
}
}

26
kmake/package.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "kmake",
"version": "1.0.0",
"description": "Kore build tool",
"main": "kmake.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Kode/kmake.git"
},
"keywords": [
"kore"
],
"author": "Robert Konrad",
"license": "Zlib",
"bugs": {
"url": "https://github.com/Kode/kmake/issues"
},
"homepage": "https://github.com/Kode/kmake#readme",
"devDependencies": {
"@types/node": "^18.15.3",
"typescript": "^4.9.5"
}
}

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);
}

24
kmake/tsconfig.json Normal file
View File

@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"sourceMap": true,
"outDir": "../lib/kmake",
"noImplicitAny": true,
"forceConsistentCasingInFileNames": true,
"newLine": "LF",
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"baseUrl": "src",
"paths": {
"kmake/*": ["./*"]
}
},
"include": [
"src/**/*.ts"
],
"exclude": [
"src/@types/**/*.ts"
],
"compileOnSave": true
}

58
kmake/tslint.json Normal file
View File

@ -0,0 +1,58 @@
{
"rules": {
"class-name": true,
"comment-format": [
true,
"check-space"
],
"indent": [
true,
"tabs"
],
"no-duplicate-variable": true,
"no-eval": true,
"no-internal-module": true,
"no-unsafe-finally": true,
"no-var-keyword": true,
"one-line": [
true,
"check-open-brace",
"check-whitespace"
],
"quotemark": [
true,
"single"
],
"semicolon": [
true,
"always"
],
"triple-equals": [
true,
"allow-null-check"
],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"variable-name": [
true,
"ban-keywords"
],
"whitespace": [
true,
"check-branch",
"check-decl",
"check-module",
"check-operator",
"check-separator",
"check-type"
]
}
}