forgor that videos and fixed banner size make this a define keep thes demo units when using TESTING_ADS only always enable it on debug builds (?) Update AdMobUtil.hx admob features Update PlayState.hx Update AdMobUtil.hx Update AdMobUtil.hx Show privacy options form if required. Update Main.hx Update Main.hx Update PlayState.hx Update AdMobUtil.hx Update AdMobUtil.hx Update AdMobUtil.hx Co-Authored-By: Mihai Alexandru <77043862+majigsaw77@users.noreply.github.com> Co-Authored-By: sector-a <82838084+sector-a@users.noreply.github.com>
1261 lines
39 KiB
Haxe
1261 lines
39 KiB
Haxe
package;
|
|
|
|
// I don't think we can import `funkin` classes here. Macros? Recursion? IDK.
|
|
import hxp.*;
|
|
import lime.tools.*;
|
|
import sys.FileSystem;
|
|
|
|
using StringTools;
|
|
|
|
/**
|
|
* This HXP performs the functions of a Lime `project.xml` file,
|
|
* but it's written in Haxe rather than XML!
|
|
*
|
|
* This makes it far easier to organize, reuse, and refactor,
|
|
* and improves management of feature flag logic.
|
|
*/
|
|
@:nullSafety
|
|
class Project extends HXProject {
|
|
//
|
|
// METADATA
|
|
//
|
|
|
|
/**
|
|
* The game's version number, as a Semantic Versioning string with no prefix.
|
|
* REMEMBER TO CHANGE THIS WHEN THE GAME UPDATES!
|
|
* You only have to change it here, the rest of the game will query this value.
|
|
*/
|
|
static final VERSION:String = "0.6.2";
|
|
|
|
/**
|
|
* The game's name. Used as the default window title.
|
|
*/
|
|
static final TITLE:String = "Friday Night Funkin'";
|
|
|
|
/**
|
|
* The name of the generated executable file.
|
|
* For example, `"Funkin"` will create a file called `Funkin.exe`.
|
|
*/
|
|
static final EXECUTABLE_NAME:String = "Funkin";
|
|
|
|
/**
|
|
* The relative location of the source code.
|
|
*/
|
|
static final SOURCE_DIR:String = "source";
|
|
|
|
/**
|
|
* The fully qualified class path for the game's preloader.
|
|
* Particularly important on HTML5 but we use it on all platforms.
|
|
*/
|
|
static final PRELOADER:String = "funkin.ui.transition.preload.FunkinPreloader";
|
|
|
|
/**
|
|
* A package name used for identifying the app on various app stores.
|
|
*/
|
|
static final PACKAGE_NAME:String = "me.funkin.fnf";
|
|
|
|
/**
|
|
* The fully qualified class path for the entry point class to execute when launching the game.
|
|
* It's where `public static function main():Void` goes.
|
|
*/
|
|
static final MAIN_CLASS:String = "Main";
|
|
|
|
/**
|
|
* The company name for the game.
|
|
* This appears in metadata in places I think.
|
|
*/
|
|
static final COMPANY:String = "The Funkin' Crew";
|
|
|
|
/**
|
|
* Path to the Haxe script run before building the game.
|
|
*/
|
|
static final PREBUILD_HX:String = "source/Prebuild.hx";
|
|
|
|
/**
|
|
* Path to the Haxe script run after building the game.
|
|
*/
|
|
static final POSTBUILD_HX:String = "source/Postbuild.hx";
|
|
|
|
/**
|
|
* Asset path globs to always exclude from asset libraries.
|
|
*/
|
|
static final EXCLUDE_ASSETS:Array<String> = [".*", "cvs", "thumbs.db", "desktop.ini", "*.hash", "*.md"];
|
|
|
|
/**
|
|
* Asset path globs to exclude on web platforms.
|
|
*/
|
|
static final EXCLUDE_ASSETS_WEB:Array<String> = ["*.ogg"];
|
|
/**
|
|
* Asset path globs to exclude on native platforms.
|
|
*/
|
|
static final EXCLUDE_ASSETS_NATIVE:Array<String> = ["*.mp3"];
|
|
|
|
//
|
|
// FEATURE FLAGS
|
|
// Inverse feature flags are automatically populated.
|
|
//
|
|
static final MOUSE_TO_TOUCH:FeatureFlag = "MOUSE_TO_TOUCH";
|
|
|
|
/**
|
|
* `-DGITHUB_BUILD`
|
|
* If this flag is enabled, the game will use the configuration used by GitHub Actions
|
|
* to generate playtest builds to be pushed to the launcher.
|
|
*
|
|
* This is generally used to forcibly enable debugging features,
|
|
* even when the game is built in release mode for performance reasons.
|
|
*/
|
|
static final GITHUB_BUILD:FeatureFlag = "GITHUB_BUILD";
|
|
|
|
/**
|
|
* `-DREDIRECT_ASSETS_FOLDER`
|
|
* If this flag is enabled, the game will redirect the `assets` folder from the `export` folder
|
|
* to the `assets` folder at the root of the workspace.
|
|
* This is useful for ensuring hot reloaded changes don't get lost when rebuilding the game.
|
|
*/
|
|
static final REDIRECT_ASSETS_FOLDER:FeatureFlag = "REDIRECT_ASSETS_FOLDER";
|
|
|
|
/**
|
|
* `-DTOUCH_HERE_TO_PLAY`
|
|
* If this flag is enabled, the game will display a prompt to the user after the preloader completes,
|
|
* requiring them to click anywhere on the screen to start the game.
|
|
* This is done to ensure that the audio context can initialize properly on HTML5. Not necessary on desktop.
|
|
*/
|
|
static final TOUCH_HERE_TO_PLAY:FeatureFlag = "TOUCH_HERE_TO_PLAY";
|
|
|
|
/**
|
|
* `-DPRELOAD_ALL`
|
|
* Whether to preload all asset libraries.
|
|
* Disabled on web, enabled on desktop.
|
|
*/
|
|
static final PRELOAD_ALL:FeatureFlag = "PRELOAD_ALL";
|
|
|
|
/**
|
|
* `-DEMBED_ASSETS`
|
|
* Whether to embed all asset libraries into the executable.
|
|
*/
|
|
static final EMBED_ASSETS:FeatureFlag = "EMBED_ASSETS";
|
|
|
|
/**
|
|
* `-DHARDCODED_CREDITS`
|
|
* If this flag is enabled, the credits will be parsed and encoded in the game at compile time,
|
|
* rather than read from JSON data at runtime.
|
|
*/
|
|
static final HARDCODED_CREDITS:FeatureFlag = "HARDCODED_CREDITS";
|
|
|
|
/**
|
|
* `-DFEATURE_DEBUG_FUNCTIONS`
|
|
* If this flag is enabled, the game will have all playtester-only debugging functionality enabled.
|
|
* This includes debug hotkeys like time travel in the Play State.
|
|
* By default, enabled on debug builds or playtester builds and disabled on release builds.
|
|
*/
|
|
static final FEATURE_DEBUG_FUNCTIONS:FeatureFlag = "FEATURE_DEBUG_FUNCTIONS";
|
|
|
|
/**
|
|
* `-DFEATURE_DEBUG_TRACY`
|
|
* If this flag is enabled, the game will have the necessary hooks for the Tracy profiler.
|
|
* Only enable this if you're using the correct fork of Haxe to support this.
|
|
* @see https://github.com/HaxeFoundation/hxcpp/pull/1153
|
|
*/
|
|
static final FEATURE_DEBUG_TRACY:FeatureFlag = "FEATURE_DEBUG_TRACY";
|
|
|
|
/**
|
|
* `-DFEATURE_LOG_TRACE`
|
|
* If this flag is enabled, the game will print debug traces to the console.
|
|
* Disable to improve performance a bunch.
|
|
*/
|
|
static final FEATURE_LOG_TRACE:FeatureFlag = "FEATURE_LOG_TRACE";
|
|
|
|
/**
|
|
* `-DFEATURE_DISCORD_RPC`
|
|
* If this flag is enabled, the game will enable the Discord Remote Procedure Call library.
|
|
* This is used to provide Discord Rich Presence support.
|
|
*/
|
|
static final FEATURE_DISCORD_RPC:FeatureFlag = "FEATURE_DISCORD_RPC";
|
|
|
|
/**
|
|
* `-DFEATURE_NEWGROUNDS`
|
|
* If this flag is enabled, the game will enable the Newgrounds library.
|
|
* This is used to provide Medal and Leaderboard support.
|
|
*/
|
|
static final FEATURE_NEWGROUNDS:FeatureFlag = "FEATURE_NEWGROUNDS";
|
|
|
|
/**
|
|
* `-DFEATURE_NEWGROUNDS_DEBUG`
|
|
* If this flag is enabled, the game will enable Newgrounds.io's debug functions.
|
|
* This provides additional information in requests, as well as "faking" medal and leaderboard submissions.
|
|
*/
|
|
static final FEATURE_NEWGROUNDS_DEBUG:FeatureFlag = "FEATURE_NEWGROUNDS_DEBUG";
|
|
|
|
/**
|
|
* `-DFEATURE_NEWGROUNDS_AUTOLOGIN`
|
|
* If this flag is enabled, the game will attempt to automatically login to Newgrounds on startup.
|
|
*/
|
|
static final FEATURE_NEWGROUNDS_AUTOLOGIN:FeatureFlag = "FEATURE_NEWGROUNDS_AUTOLOGIN";
|
|
|
|
/**
|
|
* `-DFEATURE_NEWGROUNDS_TESTING_MEDALS`
|
|
* If this flag is enabled, use the medal IDs from the debug test bench.
|
|
* If disabled, use the actual medal IDs from the release project on Newgrounds.
|
|
*/
|
|
static final FEATURE_NEWGROUNDS_TESTING_MEDALS:FeatureFlag = "FEATURE_NEWGROUNDS_TESTING_MEDALS";
|
|
|
|
/**
|
|
* `-DFEATURE_NEWGROUNDS_EVENTS`
|
|
* If this flag is enabled, the game will attempt to send events to Newgrounds when the user does stuff.
|
|
* This lets us see cool anonymized stats! It only works if the user is logged in.
|
|
*/
|
|
static final FEATURE_NEWGROUNDS_EVENTS:FeatureFlag = "FEATURE_NEWGROUNDS_EVENTS";
|
|
|
|
/**
|
|
* `-DFEATURE_FUNKVIS`
|
|
* If this flag is enabled, the game will enable the Funkin Visualizer library.
|
|
* This is used to provide audio visualization like Nene's speaker.
|
|
* Disabling this will make some waveforms inactive.
|
|
*/
|
|
static final FEATURE_FUNKVIS:FeatureFlag = "FEATURE_FUNKVIS";
|
|
|
|
/**
|
|
* `-DFEATURE_PARTIAL_SOUNDS`
|
|
* If this flag is enabled, the game will enable the FlxPartialSound library.
|
|
* This is used to provide audio previews in Freeplay.
|
|
* Disabling this will make those previews not play.
|
|
*/
|
|
static final FEATURE_PARTIAL_SOUNDS:FeatureFlag = "FEATURE_PARTIAL_SOUNDS";
|
|
|
|
/**
|
|
* `-DFEATURE_VIDEO_PLAYBACK`
|
|
* If this flag is enabled, the game will enable support for video playback.
|
|
* This requires the hxvlc library on desktop platforms.
|
|
*/
|
|
static final FEATURE_VIDEO_PLAYBACK:FeatureFlag = "FEATURE_VIDEO_PLAYBACK";
|
|
|
|
/**
|
|
* `-DFEATURE_FILE_DROP`
|
|
* If this flag is enabled, the game will support dragging and dropping files onto it for various features.
|
|
* Disabled on MacOS.
|
|
*/
|
|
static final FEATURE_FILE_DROP:FeatureFlag = "FEATURE_FILE_DROP";
|
|
|
|
/**
|
|
* `-DFEATURE_OPEN_URL`
|
|
* If this flag is enabled, the game will support opening URLs (such as the merch page).
|
|
*/
|
|
static final FEATURE_OPEN_URL:FeatureFlag = "FEATURE_OPEN_URL";
|
|
|
|
/**
|
|
* `-DFEATURE_SCREENSHOTS`
|
|
* If this flag is enabled, the game will support the screenshots feature.
|
|
*/
|
|
static final FEATURE_SCREENSHOTS:FeatureFlag = "FEATURE_SCREENSHOTS";
|
|
|
|
/**
|
|
* `-DFEATURE_CHART_EDITOR`
|
|
* If this flag is enabled, the Chart Editor will be accessible from the debug menu.
|
|
*/
|
|
static final FEATURE_CHART_EDITOR:FeatureFlag = "FEATURE_CHART_EDITOR";
|
|
|
|
/**
|
|
* `-DFEATURE_STAGE_EDITOR`
|
|
* If this flag is enabled, the Stage Editor will be accessible from the debug menu.
|
|
*/
|
|
static final FEATURE_STAGE_EDITOR:FeatureFlag = "FEATURE_STAGE_EDITOR";
|
|
|
|
/**
|
|
* `-DFEATURE_RESULTS_DEBUG
|
|
* If this flag is enabled, a debug menu for Results screen will be accessible from the debug menu.
|
|
*/
|
|
static final FEATURE_RESULTS_DEBUG:FeatureFlag = "FEATURE_RESULTS_DEBUG";
|
|
|
|
/**
|
|
* `-DFEATURE_POLYMOD_MODS`
|
|
* If this flag is enabled, the game will enable the Polymod library's support for atomic mod loading from the `./mods` folder.
|
|
* If this flag is disabled, no mods will be loaded.
|
|
*/
|
|
static final FEATURE_POLYMOD_MODS:FeatureFlag = "FEATURE_POLYMOD_MODS";
|
|
|
|
/**
|
|
* `-DFEATURE_GHOST_TAPPING`
|
|
* If this flag is enabled, misses will not be counted when it is not the player's turn.
|
|
* Misses are still counted when the player has notes to hit.
|
|
*/
|
|
static final FEATURE_GHOST_TAPPING:FeatureFlag = "FEATURE_GHOST_TAPPING";
|
|
|
|
/**
|
|
* `-DEXCLUDE_ARMV7`
|
|
* If this flag is enabled, armv7 won't be compiled when building android.
|
|
*/
|
|
static final EXCLUDE_ARMV7:FeatureFlag = "EXCLUDE_ARMV7";
|
|
|
|
/**
|
|
* `-DDISABLE_ADMOB_ADS`
|
|
* If this flag is enabled, ADs from AdMob will be disabled.
|
|
*/
|
|
static final DISABLE_ADMOB_ADS:FeatureFlag = "DISABLE_ADMOB_ADS";
|
|
|
|
/**
|
|
* `-DTESTING_ADS`
|
|
* If this flag is enabled, ADs will use testing mode.
|
|
*/
|
|
static final TESTING_ADS:FeatureFlag = "TESTING_ADS";
|
|
|
|
//
|
|
// CONFIGURATION FUNCTIONS
|
|
//
|
|
|
|
public function new() {
|
|
super();
|
|
|
|
flair();
|
|
configureApp();
|
|
|
|
displayTarget();
|
|
configureFeatureFlags();
|
|
configureCompileDefines();
|
|
configureIncludeMacros();
|
|
configureCustomMacros();
|
|
configureOutputDir();
|
|
configurePolymod();
|
|
configureHaxelibs();
|
|
configureAssets();
|
|
configureIcons();
|
|
|
|
if (DISABLE_ADMOB_ADS.isDisabled(this))
|
|
{
|
|
configureAdMobKeys();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Do something before building, display some ASCII or something IDK
|
|
*/
|
|
function flair() {
|
|
// TODO: Implement this.
|
|
info("Friday Night Funkin'");
|
|
info("Initializing build...");
|
|
|
|
info("Target Version: " + VERSION);
|
|
info("Git Branch: " + getGitBranch());
|
|
info("Git Commit: " + getGitCommit());
|
|
info("Git Modified? " + getGitModified());
|
|
info("Display? " + isDisplay());
|
|
}
|
|
|
|
/**
|
|
* Apply basic project metadata, such as the game title and version number,
|
|
* as well as info like the package name and company (used by various app stores).
|
|
*/
|
|
function configureApp() {
|
|
this.meta.title = TITLE;
|
|
this.meta.version = VERSION;
|
|
this.meta.packageName = PACKAGE_NAME;
|
|
this.meta.company = COMPANY;
|
|
|
|
this.app.main = MAIN_CLASS;
|
|
this.app.file = EXECUTABLE_NAME;
|
|
this.app.preloader = PRELOADER;
|
|
|
|
// Tell Lime where to look for the game's source code.
|
|
// If for some reason we have multiple source directories, we can add more entries here.
|
|
this.sources.push(SOURCE_DIR);
|
|
|
|
// Tell Lime to run some prebuild and postbuild scripts.
|
|
this.preBuildCallbacks.push(buildHaxeCLICommand(PREBUILD_HX));
|
|
this.postBuildCallbacks.push(buildHaxeCLICommand(POSTBUILD_HX));
|
|
|
|
// TODO: Should we provide this?
|
|
// this.meta.buildNumber = 0;
|
|
|
|
// These values are only used by the SWF target I think.
|
|
// this.app.path
|
|
// this.app.init
|
|
// this.app.swfVersion
|
|
// this.app.url
|
|
|
|
// These values are only used by... FIREFOX MARKETPLACE WHAT?
|
|
// this.meta.description = "";
|
|
// this.meta.companyId = COMPANY;
|
|
// this.meta.companyUrl = COMPANY;
|
|
|
|
// Configure the window.
|
|
// Automatically configure FPS.
|
|
this.window.fps = 60;
|
|
// Set the window size.
|
|
this.window.width = 1280;
|
|
this.window.height = 720;
|
|
// Black background on release builds, magenta on debug builds.
|
|
this.window.background = FEATURE_DEBUG_FUNCTIONS.isEnabled(this) ? 0xFFFF00FF : 0xFF000000;
|
|
|
|
this.window.hardware = true;
|
|
this.window.vsync = false;
|
|
// force / allow high DPI
|
|
this.window.allowHighDPI = true;
|
|
|
|
if (isWeb()) {
|
|
this.window.resizable = true;
|
|
}
|
|
|
|
if (isDesktop()) {
|
|
this.window.orientation = Orientation.LANDSCAPE;
|
|
this.window.fullscreen = false;
|
|
this.window.resizable = true;
|
|
this.window.vsync = false;
|
|
}
|
|
|
|
if (isMobile()) {
|
|
this.window.orientation = Orientation.LANDSCAPE;
|
|
this.window.fullscreen = true;
|
|
this.window.resizable = false;
|
|
this.window.width = 0;
|
|
this.window.height = 0;
|
|
}
|
|
|
|
if (isAndroid() && EXCLUDE_ARMV7.isEnabled(this)) {
|
|
this.excludeArchitectures.push(Architecture.ARMV7);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log information about the configured target platform.
|
|
*/
|
|
function displayTarget() {
|
|
// Display the target operating system.
|
|
switch (this.target) {
|
|
case Platform.WINDOWS:
|
|
info('Target Platform: Windows');
|
|
case Platform.MAC:
|
|
info('Target Platform: MacOS');
|
|
case Platform.LINUX:
|
|
info('Target Platform: Linux');
|
|
case Platform.ANDROID:
|
|
info('Target Platform: Android');
|
|
case Platform.IOS:
|
|
info('Target Platform: IOS');
|
|
case Platform.HTML5:
|
|
info('Target Platform: HTML5');
|
|
// See lime.tools.Platform for a full list.
|
|
// case Platform.EMSCRITEN: // A WebAssembly build might be interesting...
|
|
// case Platform.AIR:
|
|
// case Platform.BLACKBERRY:
|
|
// case Platform.CONSOLE_PC:
|
|
// case Platform.FIREFOX:
|
|
// case Platform.FLASH:
|
|
// case Platform.PS3:
|
|
// case Platform.PS4:
|
|
// case Platform.TIZEN:
|
|
// case Platform.TVOS:
|
|
// case Platform.VITA:
|
|
// case Platform.WEBOS:
|
|
// case Platform.WIIU:
|
|
// case Platform.XBOX1:
|
|
default:
|
|
error('Unsupported platform (got ${target})');
|
|
}
|
|
|
|
switch (this.platformType) {
|
|
case PlatformType.DESKTOP:
|
|
info('Platform Type: Desktop');
|
|
case PlatformType.MOBILE:
|
|
info('Platform Type: Mobile');
|
|
case PlatformType.WEB:
|
|
info('Platform Type: Web');
|
|
case PlatformType.CONSOLE:
|
|
info('Platform Type: Console');
|
|
default:
|
|
error('Unknown platform type (got ${platformType})');
|
|
}
|
|
|
|
// Print whether we are using HXCPP, HashLink, or something else.
|
|
if (isWeb()) {
|
|
info('Target Language: JavaScript (HTML5)');
|
|
} else if (isHashLink()) {
|
|
info('Target Language: HashLink');
|
|
} else if (isNeko()) {
|
|
info('Target Language: Neko');
|
|
} else if (isJava()) {
|
|
info('Target Language: Java');
|
|
} else if (isNodeJS()) {
|
|
info('Target Language: JavaScript (NodeJS)');
|
|
} else if (isCSharp()) {
|
|
info('Target Language: C#');
|
|
} else {
|
|
info('Target Language: C++');
|
|
}
|
|
|
|
for (arch in this.architectures) {
|
|
// Display the list of target architectures.
|
|
switch (arch) {
|
|
case Architecture.X86:
|
|
info('Architecture: x86');
|
|
case Architecture.X64:
|
|
info('Architecture: x64');
|
|
case Architecture.ARMV5:
|
|
info('Architecture: ARMv5');
|
|
case Architecture.ARMV6:
|
|
info('Architecture: ARMv6');
|
|
case Architecture.ARMV7:
|
|
info('Architecture: ARMv7');
|
|
case Architecture.ARMV7S:
|
|
info('Architecture: ARMv7S');
|
|
case Architecture.ARM64:
|
|
info('Architecture: ARMx64');
|
|
case Architecture.MIPS:
|
|
info('Architecture: MIPS');
|
|
case Architecture.MIPSEL:
|
|
info('Architecture: MIPSEL');
|
|
case null:
|
|
if (!isWeb()) {
|
|
error('Unsupported architecture (got null on non-web platform)');
|
|
} else {
|
|
info('Architecture: Web');
|
|
}
|
|
default:
|
|
error('Unsupported architecture (got ${arch})');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Apply various feature flags based on the target platform and the user-provided build flags.
|
|
*/
|
|
function configureFeatureFlags() {
|
|
// You can explicitly override any of these.
|
|
// For example, `-DGITHUB_BUILD` or `-DNO_HARDCODED_CREDITS`
|
|
|
|
// Should be false unless explicitly requested.
|
|
GITHUB_BUILD.apply(this, false);
|
|
FEATURE_GHOST_TAPPING.apply(this, false);
|
|
// TODO: Thanks to new changes to FlxTouch and such, this became useless. Find a way to make it work again -Zack.
|
|
MOUSE_TO_TOUCH.apply(this, false);
|
|
EXCLUDE_ARMV7.apply(this, false);
|
|
DISABLE_ADMOB_ADS.apply(this, !isMobile());
|
|
TESTING_ADS.apply(this, isDebug());
|
|
|
|
// Should be true unless explicitly requested.
|
|
HARDCODED_CREDITS.apply(this, true);
|
|
FEATURE_OPEN_URL.apply(this, true);
|
|
FEATURE_POLYMOD_MODS.apply(this, true);
|
|
FEATURE_FUNKVIS.apply(this, true);
|
|
FEATURE_PARTIAL_SOUNDS.apply(this, true);
|
|
FEATURE_VIDEO_PLAYBACK.apply(this, true);
|
|
FEATURE_STAGE_EDITOR.apply(this, true);
|
|
|
|
// Should be true on debug builds or if GITHUB_BUILD is enabled.
|
|
FEATURE_DEBUG_FUNCTIONS.apply(this, isDebug() || GITHUB_BUILD.isEnabled(this));
|
|
FEATURE_RESULTS_DEBUG.apply(this, isDebug() || GITHUB_BUILD.isEnabled(this));
|
|
|
|
// Got a lot of complains about this being turned off by default on some builds.
|
|
// TODO: Look into ways to optimize logging (maybe by using a thread pool?)
|
|
FEATURE_LOG_TRACE.apply(this, true);
|
|
|
|
FEATURE_NEWGROUNDS.apply(this, true);
|
|
FEATURE_NEWGROUNDS_DEBUG.apply(this, false);
|
|
FEATURE_NEWGROUNDS_TESTING_MEDALS.apply(this, FEATURE_NEWGROUNDS.isEnabled(this) && FEATURE_DEBUG_FUNCTIONS.isEnabled(this));
|
|
FEATURE_NEWGROUNDS_AUTOLOGIN.apply(this, FEATURE_NEWGROUNDS.isEnabled(this) && isWeb());
|
|
FEATURE_NEWGROUNDS_EVENTS.apply(this, FEATURE_NEWGROUNDS.isEnabled(this));
|
|
|
|
// Should default to true on workspace builds and false on release builds.
|
|
REDIRECT_ASSETS_FOLDER.apply(this, isDebug() && isDesktop());
|
|
|
|
// Should be true on desktop, release, non-tester builds.
|
|
// We don't want testers to accidentally leak songs to their Discord friends!
|
|
FEATURE_DISCORD_RPC.apply(this, isDesktop() && !FEATURE_DEBUG_FUNCTIONS.isEnabled(this));
|
|
|
|
// Should be true only on web builds.
|
|
// Audio context issues only exist there.
|
|
TOUCH_HERE_TO_PLAY.apply(this, isWeb() || isMobile());
|
|
|
|
// Should be true only on web builds.
|
|
// Enabling embedding and preloading is required to preload assets properly.
|
|
EMBED_ASSETS.apply(this, isWeb());
|
|
PRELOAD_ALL.apply(this, !isWeb());
|
|
|
|
// Should be true except on MacOS and Mobile.
|
|
// File drop doesn't work there.
|
|
FEATURE_FILE_DROP.apply(this, !(isMac() || isMobile()));
|
|
|
|
// Should be true except on web and mobile builds.
|
|
// Chart editor doesn't work there.
|
|
FEATURE_CHART_EDITOR.apply(this, !(isWeb() || isMobile()));
|
|
|
|
// Should be true except on web builds.
|
|
// Screenshots doesn't work there.
|
|
FEATURE_SCREENSHOTS.apply(this, !isWeb());
|
|
}
|
|
|
|
/**
|
|
* Set compilation flags which are not feature flags.
|
|
*/
|
|
function configureCompileDefines() {
|
|
// Enable OpenFL's error handler. Required for the crash logger.
|
|
setHaxedef("openfl-enable-handle-error");
|
|
|
|
// Enable stack trace tracking. Good for debugging but has a (minor) performance impact.
|
|
setHaxedef("HXCPP_CHECK_POINTER");
|
|
setHaxedef("HXCPP_STACK_LINE");
|
|
setHaxedef("HXCPP_STACK_TRACE");
|
|
setHaxedef("hscriptPos");
|
|
|
|
setHaxedef("safeMode");
|
|
|
|
// If we aren't using the Flixel debugger, strip it out.
|
|
if (FEATURE_DEBUG_FUNCTIONS.isDisabled(this)) {
|
|
setHaxedef("FLX_NO_DEBUG");
|
|
}
|
|
|
|
// Disable the built in pause screen when unfocusing the game.
|
|
setHaxedef("FLX_NO_FOCUS_LOST_SCREEN");
|
|
|
|
// HaxeUI configuration.
|
|
setHaxedef("haxeui_no_mouse_reset");
|
|
setHaxedef("haxeui_focus_out_on_click"); // Unfocus a dialog when clicking out of it
|
|
setHaxedef("haxeui_dont_impose_base_class"); // Suppress a macro error
|
|
|
|
if (isRelease()) {
|
|
// Improve performance on Nape
|
|
// TODO: Do we even use Nape?
|
|
setHaxedef("NAPE_RELEASE_BUILD");
|
|
}
|
|
|
|
// Cleaner looking compiler errors.
|
|
setHaxedef("message.reporting", "pretty");
|
|
|
|
if (FEATURE_DEBUG_TRACY.isEnabled(this)) {
|
|
setHaxedef("HXCPP_TELEMETRY"); // Enable telemetry
|
|
setHaxedef("HXCPP_TRACY"); // Enable Tracy telemetry
|
|
setHaxedef("HXCPP_TRACY_MEMORY"); // Track memory allocations
|
|
setHaxedef("HXCPP_TRACY_ON_DEMAND"); // Only collect telemetry when Tracy is open and reachable
|
|
// setHaxedef("HXCPP_TRACY_INCLUDE_CALLSTACKS"); // Inspect callstacks per zone, inflating telemetry data
|
|
|
|
setHaxedef("absolute-paths"); // Fix source locations so Tracy can see them
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set compilation flags which manage dead code elimination.
|
|
*/
|
|
function configureIncludeMacros() {
|
|
// Disable dead code elimination.
|
|
// This prevents functions that are unused by the base game from being unavailable to HScript.
|
|
addHaxeFlag("-dce no");
|
|
|
|
// Forcibly include all Funkin' classes in builds.
|
|
// This prevents classes that are unused by the base game from being unavailable to HScript.
|
|
addHaxeMacro("include('funkin', true, ['funkin.mobile.*'])");
|
|
|
|
if (isMobile()) {
|
|
addHaxeMacro("include('funkin.mobile')");
|
|
}
|
|
|
|
// Ensure all HaxeUI components are available at runtime.
|
|
addHaxeMacro("include('haxe.ui.backend.flixel.components')");
|
|
addHaxeMacro("include('haxe.ui.core')");
|
|
addHaxeMacro("include('haxe.ui.components')");
|
|
addHaxeMacro("include('haxe.ui.containers')");
|
|
addHaxeMacro("include('haxe.ui.containers.dialogs')");
|
|
addHaxeMacro("include('haxe.ui.containers.menus')");
|
|
addHaxeMacro("include('haxe.ui.containers.properties')");
|
|
|
|
// Ensure all Flixel classes are available at runtime.
|
|
// Explicitly ignore packages which require additional dependencies.
|
|
addHaxeMacro("include('flixel', true, [ 'flixel.addons.editors.spine.*', 'flixel.addons.nape.*', 'flixel.system.macros.*', 'flixel.addons.tile.FlxRayCastTilemap' ])");
|
|
}
|
|
|
|
/**
|
|
* Set compilation flags which manage bespoke build-time macros.
|
|
*/
|
|
function configureCustomMacros() {
|
|
// This macro allows addition of new functionality to existing Flixel. -->
|
|
addHaxeMacro("addMetadata('@:build(funkin.util.macro.FlxMacro.buildFlxBasic())', 'flixel.FlxBasic')");
|
|
}
|
|
|
|
function configureOutputDir() {
|
|
// Set the output directory. Depends on the target platform and build type.
|
|
|
|
var buildDir = 'export/${isDebug() ? 'debug' : 'release'}';
|
|
|
|
// we use a dedicated 'tracy' folder, since it generally needs a recompile when in use
|
|
if (FEATURE_DEBUG_TRACY.isEnabled(this))
|
|
buildDir += "-tracy";
|
|
|
|
// trailing slash might not be needed, works fine on macOS without it, but I haven't tested on Windows!
|
|
buildDir += "/";
|
|
|
|
|
|
info('Output directory: $buildDir');
|
|
// setenv('BUILD_DIR', buildDir);
|
|
app.path = buildDir;
|
|
}
|
|
|
|
function configurePolymod() {
|
|
// The file extension to use for script files.
|
|
setHaxedef("POLYMOD_SCRIPT_EXT", ".hscript");
|
|
// Which asset library to use for scripts.
|
|
setHaxedef("POLYMOD_SCRIPT_LIBRARY", "scripts");
|
|
// The base path from which scripts should be accessed.
|
|
setHaxedef("POLYMOD_ROOT_PATH", "scripts/");
|
|
// Determines the subdirectory of the mod folder used for file appending.
|
|
setHaxedef("POLYMOD_APPEND_FOLDER", "_append");
|
|
// Determines the subdirectory of the mod folder used for file merges.
|
|
setHaxedef("POLYMOD_MERGE_FOLDER", "_merge");
|
|
// Determines the file in the mod folder used for metadata.
|
|
setHaxedef("POLYMOD_MOD_METADATA_FILE", "_polymod_meta.json");
|
|
// Determines the file in the mod folder used for the icon.
|
|
setHaxedef("POLYMOD_MOD_ICON_FILE", "_polymod_icon.png");
|
|
|
|
if (isDebug()) {
|
|
// Turns on additional debug logging.
|
|
setHaxedef("POLYMOD_DEBUG");
|
|
}
|
|
}
|
|
|
|
function configureHaxelibs() {
|
|
// Don't enforce
|
|
// addHaxelib('lime'); // Game engine backend
|
|
// addHaxelib('openfl'); // Game engine backend
|
|
|
|
addHaxelib('flixel'); // Game engine
|
|
|
|
addHaxelib('flixel-addons'); // Additional utilities for Flixel
|
|
addHaxelib('hscript'); // Scripting
|
|
// addHaxelib('flixel-ui'); // UI framework (DEPRECATED)
|
|
addHaxelib('haxeui-core'); // UI framework
|
|
addHaxelib('haxeui-flixel'); // Integrate HaxeUI with Flixel
|
|
addHaxelib('flixel-text-input'); // Improved text field rendering for HaxeUI
|
|
|
|
addHaxelib('polymod'); // Modding framework
|
|
addHaxelib('flxanimate'); // Texture atlas rendering
|
|
|
|
addHaxelib('json2object'); // JSON parsing
|
|
addHaxelib('jsonpath'); // JSON parsing
|
|
addHaxelib('jsonpatch'); // JSON parsing
|
|
addHaxelib('thx.core'); // General utility library, "the lodash of Haxe"
|
|
addHaxelib('thx.semver'); // Version string handling
|
|
|
|
if (isDebug()) {
|
|
addHaxelib('hxcpp-debug-server'); // VSCode debug support
|
|
}
|
|
|
|
if (isDesktop() || isMobile() && !isHashLink() && FEATURE_VIDEO_PLAYBACK.isEnabled(this)) {
|
|
// hxvlc doesn't function on HashLink or non-desktop platforms
|
|
// It's also unnecessary if video playback is disabled
|
|
addHaxelib('hxvlc'); // Video playback
|
|
}
|
|
|
|
if (isAndroid())
|
|
addHaxelib('extension-androidtools'); // Android Functions
|
|
|
|
if (FEATURE_DISCORD_RPC.isEnabled(this)) {
|
|
addHaxelib('hxdiscord_rpc'); // Discord API
|
|
}
|
|
|
|
if (FEATURE_NEWGROUNDS.isEnabled(this)) {
|
|
addHaxelib('newgrounds'); // Newgrounds API
|
|
}
|
|
|
|
if (FEATURE_FUNKVIS.isEnabled(this)) {
|
|
addHaxelib('funkin.vis'); // Audio visualization
|
|
addHaxelib('grig.audio'); // Audio data utilities
|
|
}
|
|
|
|
if (FEATURE_PARTIAL_SOUNDS.isEnabled(this)) {
|
|
addHaxelib('FlxPartialSound'); // Partial sound
|
|
}
|
|
|
|
if (DISABLE_ADMOB_ADS.isDisabled(this)) {
|
|
addHaxelib('extension-admob'); // Ads Extension
|
|
}
|
|
}
|
|
|
|
function configureAssets() {
|
|
var exclude = EXCLUDE_ASSETS.concat(isWeb() ? EXCLUDE_ASSETS_WEB : EXCLUDE_ASSETS_NATIVE);
|
|
var shouldPreload = PRELOAD_ALL.isEnabled(this);
|
|
var shouldEmbed = EMBED_ASSETS.isEnabled(this);
|
|
|
|
if (shouldEmbed) {
|
|
info('Embedding assets into executable...');
|
|
} else {
|
|
info('Including assets alongside executable...');
|
|
}
|
|
|
|
// Default asset library
|
|
var shouldPreloadDefault = true;
|
|
addAssetLibrary("default", shouldEmbed, shouldPreloadDefault);
|
|
addAssetPath("assets/preload", "assets", "default", ["*"], exclude, shouldEmbed);
|
|
|
|
// Font assets
|
|
var shouldEmbedFonts = true;
|
|
addAssetPath("assets/fonts", null, "default", ["*"], exclude, shouldEmbedFonts);
|
|
|
|
// Shared asset libraries
|
|
addAssetLibrary("songs", shouldEmbed, shouldPreload);
|
|
addAssetPath("assets/songs", "assets/songs", "songs", ["*"], exclude, shouldEmbed);
|
|
addAssetLibrary("shared", shouldEmbed, shouldPreload);
|
|
addAssetPath("assets/shared", "assets/shared", "shared", ["*"], exclude, shouldEmbed);
|
|
if (FEATURE_VIDEO_PLAYBACK.isEnabled(this)) {
|
|
var shouldEmbedVideos = false;
|
|
addAssetLibrary("videos", shouldEmbedVideos, shouldPreload);
|
|
addAssetPath("assets/videos", "assets/videos", "videos", ["*"], exclude, shouldEmbedVideos);
|
|
}
|
|
|
|
// Level asset libraries
|
|
addAssetLibrary("tutorial", shouldEmbed, shouldPreload);
|
|
addAssetPath("assets/tutorial", "assets/tutorial", "tutorial", ["*"], exclude, shouldEmbed);
|
|
addAssetLibrary("week1", shouldEmbed, shouldPreload);
|
|
addAssetPath("assets/week1", "assets/week1", "week1", ["*"], exclude, shouldEmbed);
|
|
addAssetLibrary("week2", shouldEmbed, shouldPreload);
|
|
addAssetPath("assets/week2", "assets/week2", "week2", ["*"], exclude, shouldEmbed);
|
|
addAssetLibrary("week3", shouldEmbed, shouldPreload);
|
|
addAssetPath("assets/week3", "assets/week3", "week3", ["*"], exclude, shouldEmbed);
|
|
addAssetLibrary("week4", shouldEmbed, shouldPreload);
|
|
addAssetPath("assets/week4", "assets/week4", "week4", ["*"], exclude, shouldEmbed);
|
|
addAssetLibrary("week5", shouldEmbed, shouldPreload);
|
|
addAssetPath("assets/week5", "assets/week5", "week5", ["*"], exclude, shouldEmbed);
|
|
addAssetLibrary("week6", shouldEmbed, shouldPreload);
|
|
addAssetPath("assets/week6", "assets/week6", "week6", ["*"], exclude, shouldEmbed);
|
|
addAssetLibrary("week7", shouldEmbed, shouldPreload);
|
|
addAssetPath("assets/week7", "assets/week7", "week7", ["*"], exclude, shouldEmbed);
|
|
addAssetLibrary("weekend1", shouldEmbed, shouldPreload);
|
|
addAssetPath("assets/weekend1", "assets/weekend1", "weekend1", ["*"], exclude, shouldEmbed);
|
|
|
|
// Art asset library (where README and CHANGELOG pull from)
|
|
var shouldEmbedArt = false;
|
|
var shouldPreloadArt = false;
|
|
addAssetLibrary("art", shouldEmbedArt, shouldPreloadArt);
|
|
addAsset("art/readme.txt", "do NOT readme.txt", "art", shouldEmbedArt);
|
|
addAsset("LICENSE.md", "LICENSE.md", "art", shouldEmbedArt);
|
|
addAsset("CHANGELOG.md", "CHANGELOG.md", "art", shouldEmbedArt);
|
|
}
|
|
|
|
/**
|
|
* Configure the application's favicon and executable icon.
|
|
*/
|
|
function configureIcons() {
|
|
addIcon("art/icon16.png", 16);
|
|
addIcon("art/icon32.png", 32);
|
|
addIcon("art/icon64.png", 64);
|
|
addIcon("art/iconOG.png");
|
|
}
|
|
|
|
/**
|
|
* Configure the platforms' Ad Mob application keys.
|
|
*/
|
|
function configureAdMobKeys() {
|
|
if (isAndroid())
|
|
{
|
|
setEnv("ADMOB_APPID", "");
|
|
}
|
|
|
|
if (isIOS())
|
|
{
|
|
// This ID is not from us, it's a test ID provided by AdMob. not really sure if it works.
|
|
setEnv("ADMOB_APPID", "");
|
|
}
|
|
}
|
|
|
|
//
|
|
// HELPER FUNCTIONS
|
|
// Easy functions to make the code more readable.
|
|
//
|
|
|
|
public function isWeb():Bool {
|
|
return this.platformType == PlatformType.WEB;
|
|
}
|
|
|
|
public function isMobile():Bool {
|
|
return this.platformType == PlatformType.MOBILE;
|
|
}
|
|
|
|
public function isDesktop():Bool {
|
|
return this.platformType == PlatformType.DESKTOP;
|
|
}
|
|
|
|
public function isConsole():Bool {
|
|
return this.platformType == PlatformType.CONSOLE;
|
|
}
|
|
|
|
public function is32Bit():Bool {
|
|
return this.architectures.contains(Architecture.X86);
|
|
}
|
|
|
|
public function is64Bit():Bool {
|
|
return this.architectures.contains(Architecture.X64);
|
|
}
|
|
|
|
public function isWindows():Bool {
|
|
return this.target == Platform.WINDOWS;
|
|
}
|
|
|
|
public function isMac():Bool {
|
|
return this.target == Platform.MAC;
|
|
}
|
|
|
|
public function isLinux():Bool {
|
|
return this.target == Platform.LINUX;
|
|
}
|
|
|
|
public function isAndroid():Bool {
|
|
return this.target == Platform.ANDROID;
|
|
}
|
|
|
|
public function isIOS():Bool {
|
|
return this.target == Platform.IOS;
|
|
}
|
|
|
|
public function isHashLink():Bool {
|
|
return this.targetFlags.exists("hl");
|
|
}
|
|
|
|
public function isNeko():Bool {
|
|
return this.targetFlags.exists("neko");
|
|
}
|
|
|
|
public function isJava():Bool {
|
|
return this.targetFlags.exists("java");
|
|
}
|
|
|
|
public function isNodeJS():Bool {
|
|
return this.targetFlags.exists("nodejs");
|
|
}
|
|
|
|
public function isCSharp():Bool {
|
|
return this.targetFlags.exists("cs");
|
|
}
|
|
|
|
public function isDisplay():Bool {
|
|
return this.command == "display";
|
|
}
|
|
|
|
public function isDebug():Bool {
|
|
return this.debug;
|
|
}
|
|
|
|
public function isRelease():Bool {
|
|
return !isDebug();
|
|
}
|
|
|
|
public function getHaxedef(name:String):Null<Dynamic> {
|
|
return this.haxedefs.get(name);
|
|
}
|
|
|
|
public function setHaxedef(name:String, ?value:String):Void {
|
|
if (value == null) value = "";
|
|
|
|
this.haxedefs.set(name, value);
|
|
}
|
|
|
|
public function unsetHaxedef(name:String):Void {
|
|
this.haxedefs.remove(name);
|
|
}
|
|
|
|
public function getDefine(name:String):Null<Dynamic> {
|
|
return this.defines.get(name);
|
|
}
|
|
|
|
public function hasDefine(name:String):Bool {
|
|
return this.defines.exists(name);
|
|
}
|
|
|
|
public function setEnv(name:String, value:String = '') {
|
|
this.defines.set(name, value);
|
|
this.environment.set(name, value);
|
|
this.setenv(name, value);
|
|
}
|
|
|
|
/**
|
|
* Add a library to the list of dependencies for the project.
|
|
* @param name The name of the library to add.
|
|
* @param version The version of the library to add. Optional.
|
|
*/
|
|
public function addHaxelib(name:String, version:String = ""):Void {
|
|
this.haxelibs.push(new Haxelib(name, version));
|
|
}
|
|
|
|
/**
|
|
* Add a `haxeflag` to the project.
|
|
*/
|
|
public function addHaxeFlag(value:String):Void {
|
|
this.haxeflags.push(value);
|
|
}
|
|
|
|
/**
|
|
* Call a Haxe build macro.
|
|
*/
|
|
public function addHaxeMacro(value:String):Void {
|
|
addHaxeFlag('--macro ${value}');
|
|
}
|
|
|
|
/**
|
|
* Add an icon to the project.
|
|
* @param icon The path to the icon.
|
|
* @param size The size of the icon. Optional.
|
|
*/
|
|
public function addIcon(icon:String, ?size:Int):Void {
|
|
this.icons.push(new Icon(icon, size));
|
|
}
|
|
|
|
/**
|
|
* Add an asset to the game build.
|
|
* @param path The path the asset is located at.
|
|
* @param rename The path the asset should be placed.
|
|
* @param library The asset library to add the asset to. `null` = "default"
|
|
* @param embed Whether to embed the asset in the executable.
|
|
*/
|
|
public function addAsset(path:String, ?rename:String, ?library:String, embed:Bool = false):Void {
|
|
// path, rename, type, embed, setDefaults
|
|
var asset = new Asset(path, rename, null, embed, true);
|
|
@:nullSafety(Off)
|
|
{
|
|
asset.library = library ?? "default";
|
|
}
|
|
this.assets.push(asset);
|
|
}
|
|
|
|
/**
|
|
* Add an entire path of assets to the game build.
|
|
* @param path The path the assets are located at.
|
|
* @param rename The path the assets should be placed.
|
|
* @param library The asset library to add the assets to. `null` = "default"
|
|
* @param include An optional array to include specific asset names.
|
|
* @param exclude An optional array to exclude specific asset names.
|
|
* @param embed Whether to embed the assets in the executable.
|
|
*/
|
|
public function addAssetPath(path:String, ?rename:String, library:String, ?include:Array<String>, ?exclude:Array<String>, embed:Bool = false):Void {
|
|
// Argument parsing.
|
|
if (path == "") return;
|
|
|
|
if (include == null) include = [];
|
|
|
|
if (exclude == null) exclude = [];
|
|
|
|
var targetPath = rename ?? path;
|
|
if (targetPath != "") targetPath += "/";
|
|
|
|
// Validate path.
|
|
if (!sys.FileSystem.exists(path)) {
|
|
error('Could not find asset path "${path}".');
|
|
} else if (!sys.FileSystem.isDirectory(path)) {
|
|
error('Could not parse asset path "${path}", expected a directory.');
|
|
} else {
|
|
// info(' Found asset path "${path}".');
|
|
}
|
|
|
|
for (file in sys.FileSystem.readDirectory(path)) {
|
|
if (sys.FileSystem.isDirectory('${path}/${file}')) {
|
|
// Attempt to recursively add all assets in the directory.
|
|
if (this.filter(file, ["*"], exclude)) {
|
|
addAssetPath('${path}/${file}', '${targetPath}${file}', library, include, exclude, embed);
|
|
}
|
|
} else {
|
|
if (this.filter(file, include, exclude)) {
|
|
addAsset('${path}/${file}', '${targetPath}${file}', library, embed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add an asset library to the game build.
|
|
* @param name The name of the library.
|
|
* @param embed
|
|
* @param preload
|
|
*/
|
|
public function addAssetLibrary(name:String, embed:Bool = false, preload:Bool = false):Void {
|
|
// sourcePath, name, type, embed, preload, generate, prefix
|
|
var sourcePath = '';
|
|
this.libraries.push(new Library(sourcePath, name, null, embed, preload, false, ""));
|
|
}
|
|
|
|
//
|
|
// PROCESS FUNCTIONS
|
|
//
|
|
|
|
/**
|
|
* A CLI command to run a command in the shell.
|
|
*/
|
|
public function buildCLICommand(cmd:String):CLICommand {
|
|
return CommandHelper.fromSingleString(cmd);
|
|
}
|
|
|
|
/**
|
|
* A CLI command to run a Haxe script via `--interp`.
|
|
*/
|
|
public function buildHaxeCLICommand(path:String):CLICommand {
|
|
return CommandHelper.interpretHaxe(path);
|
|
}
|
|
|
|
public function getGitCommit():String {
|
|
// Cannibalized from GitCommit.hx
|
|
var process = new sys.io.Process('git', ['rev-parse', 'HEAD']);
|
|
if (process.exitCode() != 0) {
|
|
var message = process.stderr.readAll().toString();
|
|
error('[ERROR] Could not determine current git commit; is this a proper Git repository?');
|
|
}
|
|
|
|
var commitHash:String = process.stdout.readLine();
|
|
var commitHashSplice:String = commitHash.substr(0, 7);
|
|
|
|
process.close();
|
|
|
|
return commitHashSplice;
|
|
}
|
|
|
|
public function getGitBranch():String {
|
|
// Cannibalized from GitCommit.hx
|
|
var branchProcess = new sys.io.Process('git', ['rev-parse', '--abbrev-ref', 'HEAD']);
|
|
|
|
if (branchProcess.exitCode() != 0) {
|
|
var message = branchProcess.stderr.readAll().toString();
|
|
error('Could not determine current git branch; is this a proper Git repository?');
|
|
}
|
|
|
|
var branchName:String = branchProcess.stdout.readLine();
|
|
|
|
branchProcess.close();
|
|
|
|
return branchName;
|
|
}
|
|
|
|
public function getGitModified():Bool {
|
|
var branchProcess = new sys.io.Process('git', ['status', '--porcelain']);
|
|
|
|
if (branchProcess.exitCode() != 0) {
|
|
var message = branchProcess.stderr.readAll().toString();
|
|
error('Could not determine current git status; is this a proper Git repository?');
|
|
}
|
|
|
|
var output:String = '';
|
|
try {
|
|
output = branchProcess.stdout.readLine();
|
|
} catch (e) {
|
|
if (e.message == 'Eof') {
|
|
// Do nothing.
|
|
// Eof = No output.
|
|
} else {
|
|
// Rethrow other exceptions.
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
branchProcess.close();
|
|
|
|
return output.length > 0;
|
|
}
|
|
|
|
//
|
|
// LOGGING FUNCTIONS
|
|
//
|
|
|
|
/**
|
|
* Display an error message. This should stop the build process.
|
|
*/
|
|
public function error(message:String):Void {
|
|
Log.error('${message}');
|
|
}
|
|
|
|
/**
|
|
* Display an info message. This should not interfere with the build process.
|
|
*/
|
|
public function info(message:String):Void {
|
|
if(command != "display") { Log.info('[INFO] ${message}'); }
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An object representing a feature flag, which can be enabled or disabled.
|
|
* Includes features such as automatic generation of compile defines and inversion.
|
|
*/
|
|
abstract FeatureFlag(String) {
|
|
static final INVERSE_PREFIX:String = "NO_";
|
|
|
|
public function new(input:String) {
|
|
this = input;
|
|
}
|
|
|
|
@:from
|
|
public static function fromString(input:String):FeatureFlag {
|
|
return new FeatureFlag(input);
|
|
}
|
|
|
|
/**
|
|
* Enable/disable a feature flag if it is unset, and handle the inverse flag.
|
|
* Doesn't override a feature flag that was set explicitly.
|
|
* @param enableByDefault Whether to enable this feature flag if it is unset.
|
|
*/
|
|
public function apply(project:Project, enableByDefault:Bool = false):Void {
|
|
// TODO: Name this function better?
|
|
|
|
if (isEnabled(project)) {
|
|
// If this flag was already enabled, disable the inverse.
|
|
project.info('Enabling feature flag ${this}');
|
|
getInverse().disable(project, false);
|
|
} else if (getInverse().isEnabled(project)) {
|
|
// If the inverse flag was already enabled, disable this flag.
|
|
project.info('Disabling feature flag ${this}');
|
|
disable(project, false);
|
|
} else {
|
|
if (enableByDefault) {
|
|
// Enable this flag if it was unset, and disable the inverse.
|
|
project.info('Enabling feature flag ${this}');
|
|
enable(project, true);
|
|
} else {
|
|
// Disable this flag if it was unset, and enable the inverse.
|
|
project.info('Disabling feature flag ${this}');
|
|
disable(project, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enable this feature flag by setting the appropriate compile define.
|
|
*
|
|
* @param project The project to modify.
|
|
* @param andInverse Also disable the feature flag's inverse.
|
|
*/
|
|
public function enable(project:Project, andInverse:Bool = true) {
|
|
project.setHaxedef(this, "");
|
|
if (andInverse) {
|
|
getInverse().disable(project, false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Disable this feature flag by removing the appropriate compile define.
|
|
*
|
|
* @param project The project to modify.
|
|
* @param andInverse Also enable the feature flag's inverse.
|
|
*/
|
|
public function disable(project:Project, andInverse:Bool = true) {
|
|
project.unsetHaxedef(this);
|
|
if (andInverse) {
|
|
getInverse().enable(project, false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Query if this feature flag is enabled.
|
|
* @param project The project to query.
|
|
*/
|
|
public function isEnabled(project:Project):Bool {
|
|
// Check both Haxedefs and Defines for this flag.
|
|
return project.haxedefs.exists(this) || project.defines.exists(this);
|
|
}
|
|
|
|
/**
|
|
* Query if this feature flag's inverse is enabled.
|
|
*/
|
|
public function isDisabled(project:Project):Bool {
|
|
return getInverse().isEnabled(project);
|
|
}
|
|
|
|
/**
|
|
* Return the inverse of this feature flag.
|
|
* @return A new feature flag that is the inverse of this one.
|
|
*/
|
|
public function getInverse():FeatureFlag {
|
|
if (this.startsWith(INVERSE_PREFIX)) {
|
|
return this.substring(INVERSE_PREFIX.length);
|
|
}
|
|
return INVERSE_PREFIX + this;
|
|
}
|
|
}
|