When writing code for a particular target, it might be necessary to access certain features that are only available on that target and inaccessible in Haxe code. It is still possible to access these features using the target-specific Syntax
APIs:
js.Syntax
- JavaScript syntaxphp.Syntax
- PHP syntaxpython.Syntax
- Python syntax
Important note: Before using these functions, make sure there is no alternative available in the Haxe Standard Library. The resulting syntax can not be validated by the Haxe compiler, which may result in invalid or error-prone code in the output. When checking if a particular feature is available in the standard library, consult the API documentation. Target-specific APIs are placed into a package named after the target, e.g. JavaScript-specific APIs are in the top-level js
package. Most targets have a Lib
class in their package which provides some common methods for that target.
Injects raw code expressions. The expression is a string which may include {0}
, {1}
, {2}
, etc. - these are replaced with the corresponding arguments provided to the function. This is important if the code needs to reference Haxe fields, since the names of variables in the generated output may not correspond to their names in Haxe code. The Haxe compiler will take care of adding quotes if strings are provided. The function can also return values, although the return type is always Dynamic
.
An example of js.Syntax.code
:
var myMessage = "Is Haxe great?";
var output:Bool = js.Syntax.code("confirm({0})", myMessage);
The generated JavaScript output is:
var myMessage = "Is Haxe great?";
var output = confirm(myMessage);
Other functions are documented in the corresponding API sections. These generally include native operators that are not available in Haxe.
It is planned to implement Syntax
APIs for other targets in future releases. For the time being, it is still possible to access target-specific syntax on some of the other platforms, albeit in a less type-safe manner, using an untyped
expression:
- C++
untyped __cpp__(expr, args...)
- C#
untyped __cs__(expr, args...)
- Java
untyped __java__(expr, args...)
- Lua
untyped __lua__(expr, args...)
The arguments for these follow exactly the same semantics as Syntax.code
.
Haxe provides the ability to target JavaScript. It does so by transpiling Haxe to JavaScript. The current implementation targets ECMAScript 5. But it is possible to target lower versions using -D js-es=<value>
.
When choosing the JavaScript target, only the used Haxe code of the project (and used parts of the standard library) are transpiled to JavaScript. This results in optimal filesize which is also readable. Source maps are available and there are several ways to get debug information. The standard library aims to have same functionality across all targets.
You may want to compile Haxe to JavaScript in the following scenarios:
Client-side JavaScript Interacting with DOM elements. Haxe provides up-to-date typed interfaces to interact with the Document Object Model, allowing creation and update of DOM elements.
Haxe can be used together with existing third-party libraries and frameworks, such as jQuery, React or Vue. To access third-party frameworks with a strongly-typed API, there are extern libraries available on Haxelib. Alternatively, it is possible to create own externs (see Using external JavaScript libraries) or use the dynamic type to access any framework, see Accessing Target-specific Syntax.
Creating graphics using Canvas and WebGL Use Haxe to create graphical elements on a web page using WebGL.
Libraries like OpenFL, Heaps and Kha make use of WebGL as one of their backends.
Creating Haxe code that targets server-side JavaScript Working with server-side technology. Haxe can be used to create server-side JavaScript like Node.js.
Haxe can be a powerful tool for developing JavaScript applications. Let's look at our first sample. This is a very basic example showing the toolchain.
Create a new folder and save this class as Main.hx
.
import js.Browser;
class Main {
static function main() {
var button = Browser.document.createButtonElement();
button.textContent = "Click me!";
button.onclick = (event) -> Browser.alert("Haxe is great");
Browser.document.body.appendChild(button);
}
}
To compile, either run the following from the command line:
haxe --js js/app.js --main Main
Another possibility is to create and run (double-click) a file called compile.hxml
. In this example the hxml file should be in the same directory as the example class.
--js js/app.js
--main Main
The output will be js/app.js, which creates and adds a clickable button to the document body.
To display the output in a browser, create an HTML-document called index.html
and open it.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Haxe Application</title>
</head>
<body>
<script src="js/app.js"></script>
</body>
</html>
- Debugging in JavaScript
- Haxe/JavaScript tutorials
- Haxe JavaScript API docs
- MDN JavaScript Reference
- Haxe/Javascript documentation by Matthijs Kamstra
Starting with Haxe 4.0, Haxe can generate ES6 class definitions instead of classic function + prototype combination. This might be required when using third-party ES6 libraries, because native ES6 classes cannot be extended using the old method.
To enable ES6 generation mode, simply add -D js-es=6
to the compiler arguments.
One of significant differences between Haxe classes and ES6 classes reveals itself when inheritance is used. ES6 does not allow accessing this
in any way before calling super()
within a child class constructor, while Haxe allows calling super()
at any place in the constructor.
Even when we don't explicitly access this
before super()
in Haxe code, it can still happen when a class has fields with initializer values.
Let's take a look at the following example:
class Base {
function new() {
init();
}
function init() {
trace("base init");
}
}
class Child extends Base {
var field = "hello";
override function init() {
trace(field);
}
}
Haxe guarantees that the fields will be initialized before any constructor code is executed, and because accessing this
(for setting the field value) before the super()
call is not allowed in ES6, Haxe has to work this limitation around by transforming constructors like in the following example:
class Base {
constructor() {
if(Base._hx_skip_constructor) {
return;
}
this._hx_constructor();
}
_hx_constructor() {
this.init();
}
init() {
console.log("src/Main.hx:6:","base init");
}
}
class Child extends Base {
constructor() {
Base._hx_skip_constructor = true;
super();
Base._hx_skip_constructor = false;
this._hx_constructor();
}
_hx_constructor() {
this.field = "hello";
super._hx_constructor();
}
init() {
console.log("src/Main.hx:14:",this.field);
}
}
Base._hx_skip_constructor = false;
Note that the compiler does its best to be smart and only generates this workaround code when it's required.
One important limitation that one has to keep in mind is that it's obviously not possible for Haxe to do these transformations on extern class implementations. That's why it is impossible to access this
before calling super()
at all when extending a native extern
class. Haxe will detect these cases and emit a compile error pointing to first expression or field declaration that requires this
access.
For example:
extern class Base {
function new();
}
class Child extends Base {
var field = "hello";
}
Trying to compile this will fail with the following error: Main.hx:6: characters 3-23 : Must call super() constructor before accessing this in classes derived from an extern class with constructor
However, if the parent extern
class would not have a constructor defined, this code would compile, because the compiler knows that it is safe to call a parent's empty super()
constructor before anything else.
So, the following code:
extern class Base {
}
class Child extends Base {
var field = "hello";
}
Will generate the expected ES6 output:
class Child extends Base {
constructor() {
super();
this.field = "hello";
}
}
The externs mechanism provides access to the native APIs in a type-safe manner. It assumes that the defined types exist at run-time but assumes nothing about how and where those types are defined.
An example of an extern class is the jQuery class of the Haxe Standard Library. To illustrate, here is a simplified version of this extern class:
package js.jquery;
@:native("$") extern class JQuery {
/**
Creates DOM elements on the fly from the provided string of raw HTML.
OR
Accepts a string containing a CSS selector which is then used to match a set of elements.
OR
Binds a function to be executed when the DOM has finished loading.
**/
@:selfCall
@:overload(function(element:js.html.Element):Void { })
@:overload(function(selection:js.jquery.JQuery):Void { })
@:overload(function(callback:haxe.Constraints.Function):Void { })
@:overload(function(selector:String, ?context:haxe.extern.EitherType<js.html.Element, js.jquery.JQuery>):Void { })
public function new():Void;
/**
Adds the specified class(es) to each element in the set of matched elements.
**/
@:overload(function(_function:Int -> String -> String):js.jquery.JQuery { })
public function addClass(className:String):js.jquery.JQuery;
/**
Get the HTML contents of the first element in the set of matched elements.
OR
Set the HTML contents of each element in the set of matched elements.
**/
@:overload(function(htmlString:String):js.jquery.JQuery { })
@:overload(function(_function:Int -> String -> String):js.jquery.JQuery { })
public function html():String;
}
Note that functions can be overloaded to accept different types of arguments and return values, using the @:overload
metadata. Function overloading works only in externs.
Using this extern, we can use jQuery like this:
import js.jquery.*;
..
new JQuery("#my-div").addClass("brand-success").html("haxe is great!");
..
The package and class name of the extern class should be the same as defined in the external library. If that is not the case, rewrite the path of a class using @:native
.
package my.application.media;
@:native('external.library.media.video')
extern class Video {
..
Some JavaScript libraries favor instantiating classes without using the new
keyword. To prevent the Haxe compiler outputting the new
keyword when using a class, we can attach a @:selfCall
metadata to its constructor. For example, when we instantiate the jQuery extern class above, new JQuery()
will be outputted as $()
instead of new $()
. The @:selfCall
metadata can also be attached to a method. In this case, the method will be interpreted as a direct call to the object, illustrated as follows:
extern class Functor {
public function new():Void;
@:selfCall function call():Void;
}
class Test {
static function main() {
var f = new Functor();
f.call(); // will be outputted as `f();`
}
}
Beside externs, Typedefs can be another great way to name (or alias) a JavaScript type. The major difference between typedefs and externs is that, typedefs are duck-typed but externs are not. Typedefs are suitable for common data structures, e.g. point ({x:Float, y:Float}
). Use of a point structure typedef for function arguments allows external JavaScript functions to accept point class instances from Haxe or from another JavaScript library. It is also useful for typing JSON objects.
Deprecated: Since Haxe 4.0.0: The jQuery and SWFObject externs have been removed from the standard library. For compatibility purposes they are available in the hx3compat library. See Haxe 3 Compatibility.
Haxe version | Library | Externs location |
---|---|---|
4.0 | jQuery 1.12.1 / 2.2.1 | with hx3compat: js.jquery.* |
4.0 | SFWObject 2.3 | with hx3compat: js.swfobject.* |
3.3 | jQuery 1.12.1 / 2.2.1 | js.jquery.* |
3.3 | SWFObject 2.3 | js.swfobject.* |
3.2- | jQuery 1.6.4 | js.JQuery |
3.2- | SWFObject 1.5 | js.SWFObject |
There are many externs for other popular native libraries available on Haxelib library. To view a list of them, check out the extern tag.
This is the list of JavaScript specific metadata. For more information, see also the complete list of all Haxe built-in metadata.
Metadata | Description |
---|---|
@:expose (?Name=Class path) | Makes the class available on the window object or exports for node.js |
@:jsRequire | Generate javascript module require expression for given extern |
@:selfCall | Translates method calls into calling object directly |
It is possible to make Haxe classes or static fields available for usage in plain JavaScript.
To expose, add the @:expose
metadata to the desired class or static fields.
This example exposes the Haxe class MyClass
.
It generates the following JavaScript output:
(function ($hx_exports) { "use strict";
var MyClass = $hx_exports.MyClass = function(name) {
this.name = name;
};
MyClass.prototype = {
foo: function() {
return "Greetings from " + this.name + "!";
}
};
})(typeof window != "undefined" ? window : exports);
By passing globals (like window
or exports
) as parameters to our anonymous function in the JavaScript module, it becomes available which allows to expose the Haxe generated module.
In plain JavaScript it is now possible to create an instance of the class and call its public functions.
// JavaScript code
var instance = new MyClass('Mark');
console.log(instance.foo()); // logs a message in the console
The package path of the Haxe class will be completely exposed. To rename the class or define a different package for the exposed class, use @:expose("my.package.MyExternalClass")
When the code generated by Haxe is part of a larger JavaScript project and wrapped in a large closure it is not always necessary to expose the Haxe types to global variables.
Compiling the project using -D shallow-expose
allows the types or static fields to be available for the surrounding scope of the generated closure only.
When the code is compiled using -D shallow-expose
, the generated output will look like this:
var $hx_exports = $hx_exports || {};
(function () { "use strict";
var MyClass = $hx_exports.MyClass = function(name) {
this.name = name;
};
MyClass.prototype = {
foo: function() {
return "Greetings from " + this.name + "!";
}
};
})();
var MyClass = $hx_exports.MyClass;
In this pattern, a var statement is used to expose the module; it doesn't write to the window
or exports
object.
Modern JavaScript platforms, such as Node.js provide a way of loading objects
from external modules using the "require" function. Haxe supports automatic generation
of "require" statements for extern
classes.
This feature can be enabled by specifying @:jsRequire
metadata for the extern class. If our extern
class represents a whole module, we pass a single argument to the @:jsRequire
metadata specifying the name of the module to load:
In case our extern
class represents an object within a module, second @:jsRequire
argument specifies an object to load from a module:
The second argument is a dotted-path, so we can load sub-objects in any hierarchy.
If we need to load custom JavaScript objects in runtime, a js.Lib.require
function can be used. It takes String
as its only argument and returns Dynamic
, so it is advised to be assigned to a strictly typed variable.
Developing Flash applications is really easy with Haxe. Let's look at our first code sample. This is a basic example showing most of the toolchain.
Create a new folder and save this class as Main.hx
.
import flash.Lib;
import flash.display.Shape;
class Main {
static function main() {
var stage = Lib.current.stage;
// create a center aligned rounded gray square
var shape = new Shape();
shape.graphics.beginFill(0x333333);
shape.graphics.drawRoundRect(0, 0, 100, 100, 10);
shape.x = (stage.stageWidth - 100) / 2;
shape.y = (stage.stageHeight - 100) / 2;
stage.addChild(shape);
}
}
To compile this, either run the following from the command line:
haxe --swf main-flash.swf --main Main --swf-version 15 --swf-header 960:640:60:f68712
Another possibility is to create and run (double-click) a file called compile.hxml
. In this example the hxml file should be in the same directory as the example class.
--swf main-flash.swf
--main Main
--swf-version 15
--swf-header 960:640:60:f68712
The output will be a main-flash.swf with size 960x640 pixels at 60 FPS with an orange background color and a gray square in the center.
Run the SWF standalone using the Standalone Debugger FlashPlayer.
To display the output in a browser using the Flash plugin, create an HTML-document called index.html
and open it.
<!DOCTYPE html>
<html>
<body>
<embed src="main-flash.swf" width="960" height="640">
</body>
</html>
Embedding resources is different in Haxe compared to ActionScript 3. Instead of using [embed]
(AS3-metadata) use Flash specific compiler metadata like @:bitmap
, @:font
, @:sound
or @:file
.
import flash.Lib;
import flash.display.BitmapData;
import flash.display.Bitmap;
class Main {
public static function main() {
var img = new Bitmap( new MyBitmapData(0, 0) );
Lib.current.addChild(img);
}
}
@:bitmap("relative/path/to/myfile.png")
class MyBitmapData extends BitmapData { }
To embed external .swf
or .swc
libraries, use the following compilation options:
--swf-lib <file>
Embeds the SWF library in the compiled SWF.--swf-lib-extern <file>
Adds the SWF library for type checking but doesn't include it in the compiled SWF.
The standard compilation options also provide more Haxe sources to be added to the project:
- To add another class path use
-p <directory>
. - To add a Haxelib library use
-L <library-name>
. - To force a whole package to be included in the project, use
--macro include('mypackage')
which will include all the classes declared in the given package and subpackages.
This is the list of Flash specific metadata. For a complete list see Haxe built-in metadata.
Metadata | Description |
---|---|
@:bind | Override Swf class declaration |
@:bitmap (Bitmap file path) | _Embeds given bitmap data into the class (must extend flash.display.BitmapData ) |
@:debug | Forces debug information to be generated into the Swf even without --debug |
@:file(File path) | Includes a given binary file into the target Swf and associates it with the class (must extend flash.utils.ByteArray ) |
@:font (TTF path Range String) | Embeds the given TrueType font into the class (must extend flash.text.Font ) |
@:getter (Class field name) | Generates a native getter function on the given field |
@:noDebug | Does not generate debug information into the Swf even if --debug is set |
@:ns | Internally used by the Swf generator to handle namespaces |
@:setter (Class field name) | Generates a native setter function on the given field |
@:sound (File path) | Includes a given .wav or .mp3 file into the target Swf and associates it with the class (must extend flash.media.Sound ) |
Haxe supports compilation to Neko bytecode. Neko is a virtual machine that was originally designed for Haxe. Neko is superseded by HashLink, introduced with Haxe 3.4.
To get started with Haxe/Neko, create a new folder and save this class as Main.hx
.
To compile, either run the following from the command line:
haxe --neko hello.n --main Main
Another possibility is to create and run (double-click) a file called compile.hxml
. In this example the hxml file should be in the same directory as the example class.
--neko hello.n
--main Main
The compiler outputs a binary file called hello.n
in the current folder, which can be executed with neko hello.n
from the command line.
To get started with Haxe/PHP, create a new folder and save this class as Main.hx
.
To compile, either run the following from the command line:
haxe --php bin --main Main
Another possibility is to create and run (double-click) a file called compile.hxml
. In this example the hxml file should be in the same directory as the example class.
--php bin
--main Main
The compiler outputs in the given bin-folder, which contains the generated PHP classes that prints the traced message when you run it.
The generated PHP code is compatible with PHP 7.0.0 or later. Prior to Haxe 4, the minimum required version was PHP 5.1.0.
Define | Description |
---|---|
php-prefix=<namespace> |
Root namespace for generated PHP classes. E.g. if compiled with -D php-prefix=some.sub , then all classes will be generated in the \some\sub namespace. |
php-lib=<name> |
Select the name for the PHP lib directory. |
php-front=<name> |
Select the name for the PHP front file (default: index.php ). |
The C++ target uses various C++ compilers, which are assumed to be already installed on the system, to create native executables or libraries. The compilation happens in two phases. Firstly, the Haxe compiler generates source, header and build files in an output directory. Secondly, the hxcpp
Haxelib library is invoked to run the system compilers and linkers required to generate the ultimate result.
Before you can use the C++ target, you need to install:
hxcpp
- install withhaxelib install hxcpp
.- A C++ compiler, such as
g++
,clang++
, or MSVC++.
System compilers are supported on the three primary operating systems - Mac, Linux, and Windows.
On Mac, it is recommended that you install the latest Xcode from the Mac App Store.
On Linux, it is recommended that you use the system package manager to install the compilers.
On Windows, Microsoft Visual Studio is recommended. On Windows, you can also use gcc
-based compilers. A minimal distribution is included in a Haxelib library, and can be installed with haxelib install minimingw
.
hxcpp
can be used to compile for non-host architectures if you have a suitable cross-compiler installed. The compilers are usually supplied in the form of a Software Development Kits (SDK), or in the case of iOS devices, come with the system compiler (Xcode). Selecting which compiler to use is achieved by defining particular variables in the hxcpp
build environment. Note that the hxcpp
build tool is only responsible for producing a native executable or a native library (static or dynamic), not the complete bundling and packaging of assets and meta-data that is typically required for mobile devices. Additional Haxe libraries can be used for this task.
The hxcpp
build environment can be controlled by setting or defining variables in key-value pairs. Defining a key without a particular value is usually enough to trigger the desired behavior, and these keys are often referred to as "defines".
See Defines for a list of standard defines.
The easiest way to change the hxcpp
build environment is to pass the defines though the Haxe command line using -D
. Key-value pairs can also be passed this way, e.g.:
haxe --main Main --cpp cpp -D android -D static_link -D PLATFORM=android-9
Here:
android
is defined - causeshxcpp
to cross-compile to Android.static_link
is defined - causes a static (rather than dynamic) library to be created.- the android target platform (
PLATFORM
) is set to a particular value (android-9
). This platform information is passed on to the SDK so it can generate the appropriate code.
Advanced users can add additional defines to the system at compile time using macros. These definitions will also be passed on to the hxcpp
build tool.
The hxcpp
build tool will import all the system environment variables, so you can configure the processes using the system like:
export HXCPP_VERBOSE=
Or, on Windows:
set HXCPP_VERBOSE=1
If you are running Haxe though an IDE, some care must be taken with environment variables since the variables may be read once from the environment in which the IDE was started, rather than changing when the variables are changed on the system.
The hxcpp
build tool parses several "build files". These files are in a basic XML file format and can be used to set, or conditionally set, configuration variables. As part of the build process, the .hxcpp_config.xml
file, known as the configuration file, will be read (twice). This file is located in the user's home directory (or user's profile directory on Windows) and is the best place to configure variables that are specific to the system that rarely change, such as the location of cross-compiler SDKs. A placeholder file will be generated the first time hxcpp
is run by a user on the machine. You can see the exact location of this file in the build log if you compile with the HXCPP_VERBOSE
define.
The configuration file is read twice. The first time the vars
section is read early in the configuration process, and is used to set up and configure the location of compilers. The second time, the exes
section is read near the end of the process and allows modification of the compiler or build process based on all the available information. See build.xml
file format for details on the file format.
Configuration data can be injected into the build.xml
file that is created by Haxe. This is done attaching metadata to a Haxe class that is directly or indirectly included in the build. For example:
@:buildXml("
<target id='haxe'>
<lib name='${haxelib:nme}/lib/${BINDIR}/libnme${LIBEXTRA}${LIBEXT}'/>
</target>
<include name='${haxelib:nme}/lib/NmeLink.xml'/>
")
@:keep class StaticNme { ... }
This metadata is best for adding libraries or include paths to the build. Some notes:
- The
@:keep
metadata is added to prevent dead code elimination from removing this class. - Quoting can be a bit tricky - here the double-quotes are used for Haxe, and the single quotes are added to the
build.xml
. - Injecting a single "include" command is a good way to manage the quoting issue.
- Knowledge of the build system is required to get this right. See
build.xml
file format for details on the file format. - The
build.xml
file is read after the choice of compiler has been made, so it is generally not suitable for configuring the compiler.
The hxcpp
build.xml
build system is designed to make compiling, cross-compiling and linking easy on a large variety of operating systems and devices. It was originally designed to build the Haxe-generated C++ code but has evolved to replace the need for configuration tools in many open source libraries.
The source code for the tool lives in tools/hxcpp
in the hxcpp
repo. When compiled, it can be run with the Haxelib tool. This is usually done automatically by the Haxe compiler after the C++ code has been generated. It can be done manually like:
haxelib run hxcpp build.xml key=value ... [target]
See the hxcpp
build environment.
The compiler specification and target lists all use the same format.
- Uses XML.
- Mostly declarative list of files and flags.
- Order is important:
- Overriding values is a valid technique.
- "commands" are run as they are parsed (e.g.,
echo
).
- Conditions are possible with
if
andunless
node attributes. - Substitution is possible with
${VAR}
syntax. - The
default
target must be defined.
Most of the XML nodes support if
and unless
attributes. These will enable or disable the whole node according the existence or non-existence of a define. These can be combined with a space for "and" or two pipes for "or".
Substitution is supported via the dollars-brace syntax, and does basoc text substitution. In addition, there is some special syntax that can be used:
${VAR}
- normal replacement.${removeQuotes:VAR}
- strips surrounding quotes fromVAR
, if any.${dospath:VAR}
- convertsVAR
to path with backward slashes (as used on Windows).${dir:PathWithFilename}
- just the directory part of a filename.${this_dir}
- location of the directory containing thebuild.xml
file.
The following code is saved as example.xml
:
<xml>
<echo value="Hello ${WHO}" if="WHO" unless="SILENT" />
<echo value="You are in ${haxelib:hxcpp}" unless="WHO||SILENT"/>
<error value="Silent and who both specified" if="WHO SILENT"/>
<target id="default" />
</xml>
Some example usage:
unset SILENT
haxelib run hxcpp example.xml
haxelib run hxcpp example.xml WHO=world default
export SILENT=1
haxelib run hxcpp example.xml
haxelib run hxcpp example.xml WHO=world
Or, on Windows:
unsetenv SILENT
haxelib run hxcpp example.xml
haxelib run hxcpp example.xml WHO=world default
setenv SILENT 1
haxelib run hxcpp example.xml
haxelib run hxcpp example.xml WHO=world
The build.xml
file contains configuration, targets, compilers, linkers, and files. The details can be found in the subsections:
The top-level nodes live inside an <xml>
node, and can be:
set
- Sets a "define", define being a general variable.
<set name="name" value="1" />
setenv
- Sets anhxcpp
define and an environment variable for child processes.
<setenv name="name" value="1" />
unset
- Unsets a define.if="name"
will no longer be true.
<unset name="name" />
setup
- Used internally to call custom setup code to find SDKs etc.
<setup name="androidNdk|blackberry|msvc|pdbserver|mingw|emscripten|nvcc" />
echo
- Prints a value to console. Good for debugging.
<echo value="text" />
error
- Prints a value to console and forces an error. Good for checking prerequisites.
<error value="error message" />
pleaseUpdateHxcppTool
- Used to tell people updating git version that they need to recompile the build tool.
<pleaseUpdateHxcppTool version="1" />
path
- Adds a directory to the exe search path.
<path name="directory_to_add" />
mkdir
- Creates a directory.
<mkdir name="directory" />
section
- Groups blocks of elements - usually ones that all respect the sameif
/unless
condition.
<section name="id" />...</section>
copy
- Copies a file when the node is parsed.
<copy to="destination" from="src" />
-
import
/include
- Reads XML from another file.import
resets the relative base to the new file,include
does not.noerror
- whentrue
, allows the file to be missing. (optional)section
- only read the named section from the XML file. Used byhxcpp_config.xml
. (optional)
<import name="filename" section="filter" noerror="true" />
<include name="filename" section="filter" noerror="true" />
pragma
- Only include build file once, even with multiple include statements.
<pragma once="true" />
nvccflag
- Adds flag to allnvcc
compiles.
<nvccflag name="?name" value="-IincludePath" />
nvcclinkflag
- Adds flag when linking withnvcc
code.
<nvcclinkflag name="?name" value="-arch=sm_30" />
-
files
- Defines a file group, and sets default tags.dir
- directory to which the filenames in the group are relative.tags
- comma-separated list of tags.
<files dir="dir" name="name" tags="tag1,tag2,tag3" >...</files>
target
- Defines a target, and sets itstoolid
(link mode) and output name.
<target name="name" overwrite="true" append="true" tool="linker" toolid="${haxelink}" output="filename">...</target>
copyFile
- Copies a file after the giventoolId
is run into target output directory.
<copyFile name="destination" from="src" allowMissing="true" overwrite="true" toolId="filter" />
magiclib
- Internally used for replacing DLLs with object files.
<magiclib name="libname" replace="old dll" />
-
compiler
- Defines a compiler. It is assumed only 1 compiler is active.replace
- whentrue
the compiler is overridden, otherwise the contents of this node are appended to an existing compiler with the sameid
. (optional)exe
- can be overridden in the body of the definition.
<compiler id="id" exe="command" replace="true">...</compiler>
-
stripper
- Defines a stripper, used to remove debug information for release from gcc executables.replace
- whentrue
overwrites, otherwise appends. (optional)
<stripper exe="command" replace="true">...</stripper>
-
linker
- Defines a linker.replace
- whentrue
overwrites, otherwise appends. (optional)id
- can bestatic_link
,dll
, orexe
. Usually all 3 linkers are defined.exe
- can be overridden in the body of the definition.
<linker id="id" exe="command" replace="true">...</linker>
-
prelinker
- Defines a prelinker.replace
- whentrue
overwrites, otherwise appends.
<prelinker name="id" replace="true" />...</prelinker>
The files node defines a group of files that all share the same attributes, including relative directory, default compiler flags and dependencies. The node can be used to define a set of header files on which other files can depend, or a set of source files to be compiled and included in a target.
-
depend
- Declares that all files in the group depend on another file or another file group.name
- if the named file changes then all the files in the group need recompiling.files
- if any of the files in the named group changes then then all the files in the group need recompiling.
<depend name="filename" />
<depend files="filesId" />
options
- Sets the name of the file containing compiler flags. When the cache is not used,Options.txt
helps detect when the options have changed, and therefore whether files need recompiling.
<options name="Options.txt" />
config
- Sets the name of the file to generate that contains the C++ preprocessor defines that were active when code was compiled.
<config name="outfile.h" />
tag
- Adds a default compiler tag for all files in the group. See Tags.
<tag value="tag" />
addTwice
- When compiled to a library, adds the library twice to the link line - once at the beginning and once at the end to satisfy Linux selective linking.
<addTwice value="true" />
-
cache
- Enables compiler cache for files in the group. See compile cache for more details.project
- name of project used to manage and group object files in the cache.asLibrary
- link the objects (.obj
) into a.lib
file, which can skip unneeded objects, but will also skip things that rely on static initializers to register handlers, so be careful.
<cache value="true" project="name" asLibrary="true" />
include
- Includes an external file list.
<include name="filename.xml" />
section
- Groups blocks of elements - usually ones that all respect the sameif
/unless
condition.
<section name="id" />...</section>
-
compilerflag
- Adds a compiler flag when compiling files in the group.name
andvalue
- adds 2 command-line arguments when compiling.value
- adds 1 command-line argument when compiling.
<compilerflag name="name" value="value" />
<compilerflag value="value" />
nvcc
- This group is compiled with nvcc.
<nvcc />
objprefix
- Sets the id prepended to generated object (.obj
) name to allow alphabetical grouping of similar objects.
<objprefix value="prefix" />
-
precompiledheader
- Use a precompiled header of given name when compiling the group.-
name
- the include used when precompiling these files (without the .h). -
directory
- the location of this file. -
e.g. for
#include <lib/Header.h>
:name="lib/Header"
directory="${haxelib:somelib}/include"
-
<precompiledheader name="name" dir="directory" />
-
file
- Adds a file to the group, with optional attributes.name
- name of the file - may be absolute or relative todir
.tags
- overrides group tags. See Tags. (optional)filterout
- allows files to be skipped at compile-time if the named define exists. This is useful when the define is set sometime after the file list is parsed.embed
- causes the file to be embedded as an extern C++const char *
string constant of the specified name.<depend name="..." />
(child node) - filename of additional dependency.
<file name="filename" tags="tag,tag1" filterout="define" embedName="embed" >
<depend name="filename1" />
<depend name="filename2" />
</file>
Tags are identifiers that link compiler flags with specific files. Usually, they are defined in a files
group with the tags
attribute as a comma-separated list, and with the tag
attribute on a compiler flag
node. Files are then compiled with all the flags that have matching tags.
By restricting tags to certain files, only a sub-set of files needs to be recompiled when conditions change, and files without the relevant tags can reuse their object files. This can save a lot of compilation time, since some flags only apply to a few files.
Files can override the group tags by specifying their own tags
attribute. Groups can add tags with the tag
node.
Some tags have standard meanings when compiling Haxe code:
-
haxe
- adds all the required compiler flags to get Haxe-generated code to compile correctly, and should be added to all files that depend directly or indirectly onhxcpp.h
. -
static
- will add theSTATIC_LINK
define when appropriate, which is used for generating CFFI glue code. It should be added to CFFI code that might generate static libraries. -
gc
- These flags only affect the garbage-collection files. -
hxstring
- These flags only affectString.cpp
. -
optimization tags - each file is assumed to have exactly 1 optimization tag. If none is explicitly specified, then the default is used depending on whether it is a debug or release build. They are:
optim-std
- alias fordebug
orrelease
depending on build.debug
release
optim-none
optim-size
- optimize for size.
Setting one of these tags is useful for compiling your library in release mode, even if haxe has -debug
. Some very big files are slow to compile in release mode, so using a less optimized mode can be faster.
The tags attribute can be added to a Haxe-generated file using the @:fileXml
meta, e.g.:
@:fileXml("tags='haxe,optim-none'")
class MyClass { ... }
Here, the class is compiled with the normal Haxe flags, but has optimizations disabled, which can lead to much faster compilation times in some circumstances.
Targets are used to produce binaries, or to group other targets. When compiling executables or dynamic libraries, they provide the additional link libraries.
By default, hxcpp
will try to compile the default
target, so it is easiest to define this one - perhaps by simply adding a dependence on your other target(s).
The target is defined with a toolid
attribute: exe
, static_link
, or dll
. This defines which linker is used, but many of the target entries will be the same even if the linker is changed.
Targets can contain the following nodes:
subTargetName
- Builds another target before building this one.
<target id="subTargetName" />
merge
- Combines fields from another target. This is useful if you want a target to function as a static or dynamic library when compiled on its own, but also allow it to be used as a list of object files if another target wants to link in the object files directly.
<merge id="otherTargetName" />
files
- Adds a named group of compiled files to the target.
<files id="filesId"/>
section
- Groups blocks of elements - usually ones that all respect the sameif
/unless
condition.
<section>...</section>
-
lib
- Adds a library to the link line.name
- the complete name is specified.base
- the name without a compiler-specific extension (.a/.lib) is specified.hxbase
- the name without an extension and architecture (-v7
/.iphonesim
) is specified.
<lib (name|hxbase|base)="libName" />
flag
- Adds a single link flag.
<flag value="flag"/>
vflag
- Adds a name-value pair of link arguments.
<vflag name="flag1" value="flag2"/>
depend
- Declares that the target depends on the given filename.
<depend name="filename" />
dir
- Adds a directory to the target's directory list. These directories will get removed when the target is cleaned.
<dir name="tempPath" />
outdir
- Sets the directory for build results - including "copyFile" targets.
<outdir name="path"/>
-
ext
- Sets the extension for the generated files.ext
- the extension, should contain ".".
<ext name=".ext"/>
builddir
- Sets the directory from which the target's build commands are run, and therefore the relative base for some filenames, and destination for some compiler-generated temporary files.
<builddir name="path"/>
-
libpath
- Add a library search path to the build command.name
- directory. The linker will add the required flags.
<libpath name="directory"/>
Compilers are run over each of the changed files in each of the file groups in a target to create object files, which are then linked into the target. Modification dates or hashes are used to tell if files need recompiling, or if the object file can be reused.
-
flag
- Adds a single argument to the compiler command line.value
- flag added to the command line.tag
- optional filter to restrict flag to files with a matching tag. See Tags.
<flag value="value" tag="tag" />
-
cflag
/cppflag
/objcflag
/mmflag
- Adds a flag when compiling specific file types.cflag
- only added to.c
(C) files.cppflag
- only added to.cpp
(C++) files.objcflag
- only added to.objc
(Objective-C) files.mmflag
- only added to.mm
(Objective-C++) files.
<cflag value="value" />
<cppflag value="value" />
<objcflag value="value" />
<mmflag value="value" />
-
pchflag
- Adds a flag when compiling precompiled header.h
files.pchflag
- usually["-x", "c++-header"]
for apple to specify the "identity" of the header.
<pchflag value="value" />
pch
- Sets the precompiled header style -gcc
ormsvc
.
<pch value="gcc|msvc" />
-
objdir
- Sets the name of the directory used to store object files. Should be unique for a given set of compiler flags to avoid linking against the wrong architecture.value
- usually built programmatically, e.g.obj/msvc${MSVC_VER}-rt${OBJEXT}${OBJCACHE}${XPOBJ}
.
<objdir value="obj/somewhere" />
-
output
- Sets the flag used to specify the compiler's output.value
- flag value. Note that it should contain a space character if the actual name should be a separate argument, like-o<space>
, or-o
/-out:
if the name should be appended directly.
<outflag value="-flag" />
-
exe
- Overrides the executable command specified in the compiler attribute.name
- command. Usually you would usepath
to add the directory containing the executable, then specify the filename part only in this attribute.
<exe name="command" />
-
ext
- Sets the object file extension.name
- extension, including "."..o
on Linux and Mac,.obj
on Windows.
<ext name=".obj" />
-
getversion
- Sets the command used to obtain the version of the compiler. This is used for checking if the compiler has changed, and therefore whether the objects need recompiling.value
- command to run. It defaults to(compiler) --version
which works forgcc
-based compilers. Setting it to an empty value will disable caching.
<getversion value="command" />
section
- Groups blocks of elements - usually ones that all respect the sameif
/unless
condition.
<section>...</section>
include
- Includes compiler options from another file. Most compilers should include<include name="toolchain/common-defines.xml" />
to add the defines used byhxcpp
.
<include name="filename" />
Generally only one linker is run per target to build a static library, dynamic library, or executable. The id
attribute of the linker specifies which type of linking is performed.
exe
- Overwrites the command for this linker.
<exe name="command" />
flag
- Adds a single link flag.
<flag value="flag"/>
-
ext
- Default extension for generated files - if not overridden by target.- value - extension, including
.
.
- value - extension, including
<ext value=".ext"/>
-
outflag
- Sets the flag for specifying linker output name.value
- flag value. Note that it should contain a space character if the actual name should be a separate argument, like-o<space>
, or-o
/-out:
if the name should be appended directly.
<outflag value="-o"/>
section
- Groups blocks of elements - usually ones that all respect the sameif
/unless
condition.
<section>...</section>
libdir
- Sets the temporary directory name to build into. This will capture the extra files the compiler generates, then the desired file will be copied to the correct location.
<libdir name="name"/>
-
lib
- Adds a library to the link line.name
- the complete name is specified.base
- the name without a compiler-specific extension (.a/.lib) is specified.hxbase
- the name without an extension and architecture (-v7
/.iphonesim
) is specified.
<lib (name|hxbase|base)="libName" />
-
prefix
- Sets the prefix for generated files.value
- prefix. This will usually belib
or nothing.
<prefix value="lib"/>
ranlib
- Specifies whetherranlib
needs to be run, and what command to use. Usually used only for Unix-style static libraries.
<ranlib name="ranlib command"/>
libpathflag
- Sets the flag used for adding library paths to command line. It will be combined withlib
entries.
<libpathflag value="-L"/>
recreate
- Specifies whether to delete the target file before re-running the link command. The archivear
command sometimes adds object files to existing archives, so deleting first can help.
<recreate value="true"/>
expandAr
- Specifies whether to extract the individual object files from an archive and add these, rather than add the archive as a single library. Can solve some link-order and static-initialization issues, but may make the final executable bigger.
<expandAr value="true"/>
-
fromfile
- If the linker supports taking a list of objects in a single file, then this is flag for specifying the file.value
- flag for specifying the file. If the filename should be a separate argument, then the flag should end with a space. Usually@
or-filelist
. Set to empty to disable.needsQuotes
- whether to quote object names in the file.
<fromfile value="flag" needsQuotes="true" />
A stripper is used to remove debug information in release builds on gcc
-like systems.
exe
- Overrides the stripper command.
<exe name="command"/>
flag
- Adds a flag to the stripper command.
<flag value="flag"/>
When compiling from Haxe via hxcpp
, a Build.xml
file is generated in the output directory. The file lists the generated files and dependencies and ultimately includes the toolchain/haxe-target.xml
file, which describes how to combine these files.
There are a few complications when setting the order in which things are configured, since a particular build might want to:
- set the compiler
- override some compiler settings
- make decisions based on the compiler or settings
- set or use standard file prefixes/suffixes
You can see which files are included by setting the HXCPP_VERBOSE
define when compiling. One example is for a hypothetical user 'Hugh' on a Windows platform:
Using makefile: Build.xml
No specified toolchain
- Parsing include: C:/Users/Hugh/dev/hxcpp/toolchain/setup.xml
- Parsing include: C:\Users\Hugh\.hxcpp_config.xml (section "vars")
Using Windows compiler: MSVC
- Parsing include: C:/Users/Hugh/dev/hxcpp/toolchain/finish-setup.xml
- Parsing makefile: C:\Users\Hugh\test\proj\cpp\Build.xml
- Parsing include: C:/Users/Hugh/dev/hxcpp/build-tool/BuildCommon.xml
- Parsing include: C:/Users/Hugh/dev/hxcpp/toolchain/haxe-target.xml
- Parsing include: C:/Users/Hugh/dev/hxcpp/toolchain/msvc-toolchain.xml
- Parsing compiler: C:/Users/Hugh/dev/hxcpp/toolchain/common-defines.xml
- Parsing include: C:\Users\Hugh\.hxcpp_config.xml (section "exes")
setup.xml
is read because no custom toolchain is specified, and this just includes.hxcpp_config.xml
.- The
vars
section of.hxcpp_config.xml
is read - mainly to setup SDK locations. - The
hxcpp
build tool then uses internal logic based on the host and defines to work out which compiler/toolchain to use. In this case,MSVC
(Microsoft Visual Studio C++) is selected. finish-setup.xml
then sets some standard defines for file extensions and linking flags based on the type of toolchain being used.- The provided build file (
Build.xml
) is then processed. It can use the standard defines fromfinish-setup.xml
. Build.xml
imports the standard Haxe target build files (haxe-target.xml
), which adds the files required for the Haxe runtime.- The toolchain file (
msvc-toolchain.xml
) is then parsed - making use of any settings from the mainBuild.xml
andfinish-setup.xml
. This file includescommon-defines.xml
inject standard Haxe compiler flags. - The
exes
section of.hxcpp_config.xml
is read. Historically to add libraries or build-paths to targets.
finish-setup.xml
is where the conventions are set. These variables include:
-
haxelink
-dll
,static_link
, orexe
-
LIBPREFIX
-lib
or empty -
LIBEXTRA
:.iphonesim-64
.iphonesim
.iphoneos
.iphoneos-v7
.iphoneos-v7s
.iphoneos-64
.appletvsim-64
.appletvsim
.watchos
.watchsimulator
-x86
-v7
-64
-
HX_LINK_SUFFIX
-LIBEXTRA
,-19
(MSVC 19) -
LIBEXT
-.a
,.lib
-
DBG
--debug
or empty -
OBJEXT
--list-of-config-ids
depending on available options
These variables are used by haxe-target.xml
- you can use them too. Haxe then builds the haxe
target, which uses some code like:
<set name="HAXE_OUTPUT_FILE" value="${LIBPREFIX}${HAXE_OUTPUT_PART}${DBG}" />
<target id="haxe" tool="linker" toolid="${haxelink}" output="${HAXE_OUTPUT_FILE}">
<ext value="${LIBEXTRA}.a" if="static_link" />
<!-- ... -->
<files id="__main__" unless="static_link" />
<files id="__lib__" if="static_link"/>
<files id="__resources__" />
<files id="__externs__" />
<files id="runtime" unless="dll_import" />
<files id="cppia" if="scriptable" />
<lib name="-lpthread" if="linux" unless="static_link" />
<lib name="-ldl" if="linux" unless="static_link" />
</target>
Here you can see the various file groups, which are enabled or disabled depending on the compiler mode, and some standard libraries that are needed for Linux.
If you are using external C++ code or using metadata to inject XML into the build process, and you are getting a compiler or linker error, then it can be useful to run the hxcpp
build tool without re-running Haxe. This lets you manually edit the build file or generated C++ code until you get things to work. Once you have solved the issues using this technique, then you can move the changes back into the injected/external code.
First, invoke the Haxe compiler with the -v
flag. This gives quite a bit of debug info, but will include a line like this:
haxelib run hxcpp Build.xml haxe -Dhaxe3="1" -Dhaxe_ver="4.000" -Dhxcpp_api_level="332" -Dsource-header="Generated by Haxe 4.0.0" -I"" -I"C:/Users/Hugh/dev/haxe/std/cpp/_std/" -I"C:/Users/Hugh/dev/haxe/std/"
To use this, first change directories to your output directory. This is the directory specified in the --cpp
Haxe compiler option. Then copy and paste the haxelib run ...
command into a shell, command or batch file.
When using external code in hxcpp
, it is often useful to add libraries, include paths or compiler flags to the build process. This can be done with the @:buildXml
class metadata. E.g.:
@:buildXml("
<target id='haxe'>
<lib name='${haxelib:nme}/lib/${BINDIR}/libnme${LIBEXTRA}${LIBEXT}'/>
</target>
")
@:keep
class StaticNme { ... }
By referencing the given class (by simply import
-ing it, no need to use it properly because it has the @:keep
metadata), the XML fragment is also included.
Here, the XML fragment is copied verbatim into the generated Build.xml
immediately after the standard file lists. This example adds a library to the haxe target, but you could also add flags to files
nodes, or files to another files
node or target. Another possibility is to add an include
command to pull in a whole external XML file. This can help avoid some syntax awkwardness needed when quoting strings in metadata, and allows a normal XML editor to be used.
It is also possible to replace the __main__
file group to skip the standard initialization code and use a custom bootstrap procedure.
There are a number of standard defines you can use to control the hxcpp
build. Some of these are used by the Haxe compiler, and affect the generated code. Others apply to the build tool and affect how the code is compiled.
Defines affecting how the code is generated. These need to be in the command line when calling haxe.
Define | Meaning |
---|---|
HXCPP_DEBUGGER |
Add extra macros required by debugger. Usually added automatically by the debugger haxelib. |
HXCPP_GC_GENERATIONAL |
Enable experimental generational garbage collector. |
annotate_source |
Add additional annotations to the generated source code - useful for developing hxcpp . |
dll_export |
Export hxcpp runtime symbols. |
file_extension |
Set the extension (without the dot) of generated files. E.g. -D file_extension=mm for Objective-C++ code. |
force_native_property |
Make dynamic access to fields call property getters/setters where appropriate. |
include_prefix |
Place all generated include files in a sub-directory, e.g. -D include_prefix=hxinc . Useful for avoiding name clashes. |
no-compilation |
Generate code, but do not compile it. |
no-debug |
Do not generate debug macros in code |
nocppiaast |
Use legacy cppia generation instead of new, more recent changes. |
objc |
Generate Objective-C++ classes. |
scriptable |
Enable extra runtime information required for scripting. |
Defines affecting how the code is compiled. These can be on the command line when calling haxe, or added via the hxcpp
build environment.
Define | Meaning |
---|---|
HXCPP_GC_MOVING |
Allow garbage collector to move memory to reduce fragmentation. |
HXCPP_GC_SUMMARY |
Print small profiling summary when the program exits. |
HXCPP_GC_DYNAMIC_SIZE |
Monitor GC times and expand memory working space if required. |
HXCPP_GC_BIG_BLOCKS |
Allow working memory greater than 1 gigabyte. |
HXCPP_GC_DEBUG_LEVEL |
Specify a level (1-4 ) indicating additional debugging in GC. |
HXCPP_DEBUG_LINK |
Add symbols to final binary, even in release mode. |
HXCPP_STACK_TRACE |
Have valid function-level stack traces, even in release mode. |
HXCPP_STACK_LINE |
Include line information in stack traces, even in release mode. |
HXCPP_CHECK_POINTER |
Add null-pointer checks, even in release mode. |
HXCPP_PROFILER |
Add profiler support. |
HXCPP_TELEMETRY |
Add telemetry support. |
HXCPP_CPP11 |
Use C++11 features and link libraries. |
exe_link |
Generate executable file (rather than dynamic library on Android). |
static_link |
Generate static library. |
dll_link |
Generate dynamic library. |
Define | Meaning |
---|---|
HXCPP_VERBOSE |
Print extra output from build tool. |
HXCPP_TIMES |
Show some basic profiling information. |
HXCPP_NEKO_BUILDTOOL |
Force use of hxcpp.n , rather than the compiled BuildTool.exe . |
HXCPP_NO_COLOR |
Do not add color-codes to build tool output. |
HXCPP_KEEP_TEMP |
Do not delete the files created for file embedName option. |
Define | Meaning |
---|---|
HXCPP_M32 |
Force 32-bit compile for current desktop. |
HXCPP_M64 |
Force 64-bit compile for current desktop. |
HXCPP_ARMV6 |
Compile ARM-based devices for armv6 . |
HXCPP_ARM64 |
Compile ARM-based devices for 64 bits. |
HXCPP_ARMV7 |
Compile ARM-based devices for armv7 . |
HXCPP_ARMV7S |
Compile ARM-based devices for armv7s . |
HXCPP_LINUX_ARMV7 |
Compile for a Linux ARMv7 device. |
HXCPP_LINUX_ARM64 |
Compile for a Linux ARM64 device. |
winrt |
Compile for windowsRt/windows UWP. |
android |
Compile for Android. |
PLATFORM |
Specify the Android platform for NDK compilation. |
ANDROID_NDK_ROOT |
Specify the location of the Android NDK toolchain. |
ANDROID_NDK_DIR |
Specify the search location for finding the android NDK toolchain. |
HXCPP_X86 |
Compile android for x86 architecture. |
iphoneos |
Compile for iPhone iOS. |
iphonesim |
Compile for iPhone simulator. |
appletvos |
Compile for Apple tvOS. |
appletvsim |
Compile for Apple tvOS simulator. |
watchos |
Compile for Apple watchOS. |
watchsimulator |
Compile for Apple watchOS simulator. |
webos |
Compile for webOS. |
tizen |
Compile for Tizen. |
blackberry |
Compile for Blackberry- |
emscripten |
Compile for Emscripten. |
cygwin |
Compile for Windows using Cygwin. |
linux |
(Cross) Compile for Linux. |
rpi |
(Cross) Compile for Raspberry Pi. |
mingw |
Compile for Windows using MinGW. |
HXCPP_MINGW |
Compile for Windows using MinGW. |
NO_AUTO_MSVC |
Do not detect MSVC location, use the one already in the executable path. |
The hxcpp
compile cache is used to share object files between projects. This can alleviate the need to ship static libraries with external projects, since developers who use the library can compile the library just once, and then reuse it between projects.
The cache uses a hashing mechanism to tell if the contents of a file or its dependencies have changed, and combines this with the compiler version and flags to make specific object files for each change and each compiler configuration. This also allows some common Haxe runtime and Haxe-generated files to share their object files between projects, even if in different directories.
Additional benefits include keeping some files outside the source tree, and being able to remove these temporary files easily.
A directory needs to be set aside to enable the cache. If possible, this should be on fast storage, such as an SSD. This is most easily done with an entry in the .hxcpp_config.xml
file:
<set name="HXCPP_COMPILE_CACHE" value="c:/hxcpp/cache" />
<set name="HXCPP_CACHE_MB" value="4000" />
Keeping the path short can help in some border-line cases with some compilers where command-line length can become an issue.
The cache size defaults to 1 gigabyte. For many cases, this is big enough. However, on large projects, with several architectures and a lot of debug information, this default can lead to "cache churn" where some files are evicted from the cache, even though they are likely to be used again. Increasing the number of space allocated to the cache can help.
To use the cache with your own libraries, the files group should have a cache
entry to tell hxcpp
that you have considered dependency issues when designing the group.
<cache value="true" project="name" asLibrary="true" />
See Files - cache
for more details.
When compiling normally, hxcpp
will check the cache size and evict the least used files to maintain the specified cache size. Object files in the cache are grouped into "projects" to make management easier, and the hxcpp
build tool can be used to explicitly manage the object files.
haxelib run hxcpp cache [command] [project]
Perform command on cache, either on specific project or all. commands:
clear -- remove all files from cache
days #days -- remove files older than "days"
resize #megabytes -- Only keep #megabytes MB
list -- list cache usage
details -- list cache usage, per file
Start with:
haxelib run hxcpp cache list
To get an idea of cache usage.
hxcpp
uses a conservative stop-the-world GC, where the threads need to co-operate.
- Threads must not change GC pointers in the collection phase.
- The thread stacks/registers must be scanned for GC pointers.
- Threads must not block without letting the GC system know not to wait for them, otherwise GC blocks until the end of block.
- Call
hx::GCEnterBlocking()
/gc_enter_blocking()
/cpp.vm.Gc.enterGCFreeZone()
from Haxe before a potentially blocking system call (filesystem, network, etc). - Call
hx::GCExitBlocking()
/gc_exit_blocking()
/cpp.vm.Gc.exitGCFreeZone()
from Haxe before making more GC calls. - Might need to pre-allocate buffers.
- Don't forget to exit blocking on error conditions.
- Call
When you create a thread from Haxe, it starts attached. Before a non-Haxe created thread can interact with hxcpp
, some care must be taken, since GC allocations are done using a GC context per thread, and all threads must respect the stopped world.
- Foreign threads must be attached-detached
SetTopOfStack(int * inTop,bool inPush)
inTop
- pointer to top of stack to attach, or0
to remove stack.inPush
- usuallytrue
. Recursive attachment/detachment.
- Must not change things when the world is stopped.
- Must define their stack range for scanning.
- If you are attached, you may need to enter/exit GC-free zone.
- Must release context when done, if no more calls are going to be made.
- Make sure local variables are covered in the stack.
- Compiler may reorder, so be careful.
- Read documentation because some things, e.g. audio callbacks, happen on other threads.
- You can use other techniques, e.g.:
- Create a Haxe thread, which blocks waiting for a signal.
- Foreign thread generates request and signals Haxe thread.
- Haxe thread performs job and generates data then signals foreign thread.
- Foreign thread picks up data and carries on.
- To understand how to handle threads, you need a mental picture of the C++ stack.
- The stack usually grows "down". That is, if the first stack location is 10000, the next one will be 9999 etc.
- Historical, but consistent. Except for
emscripten
which goes up - but still use same terminology/picture, just change the less-thans to greater-thans in code.
Say the system starts each program stack at 10000, the stack might look like this, with local variables and arguments pushed on the stack:
10000
-----------------------------------------------
9996 startup temp variable
9992 startup temp variable
-- main function --
9988 main return address - order and details of this are ABI specific
9984 char ** argv
9980 int argc
hxcpp
then runs the main code, which starts with the macro HX_TOP_OF_STACK
, which expands to something like:
int t0 = 99;
hx::SetTopOfStack(&t0,false);
...
__boot_all();
__hxcpp_main();
-- main function --
9988 main return address order and details of this are ABI specific
9984 char ** argv
9980 int argc
9976 int t0
-- hx::SetTopOfStack --
records '9976' as top of stack for this thread
Later, many generated functions deep, __hxcpp_main
generates an allocation call which triggers a collection.
...
8100 Array<Bullet> bullets
-- alloc Enemy --
...
-- Call collect --
8050 int bottomOfStackTemp
MarkConservative(&bottomOfStackTemp, 9976) -> scans stack from 8050 -> 9976
MarkConservative(Capture registers)
Enter/exit use a similar technique, where the registers are captured and the bottomOfStack
is 'locked-in' when the "enter GC-free zone" call is made.
8100 Array<Bullet> bullets
-- EnterGCFreeZone --
8088 int bottomOfStackTemp
thread->setBottomOfStack(&bottomOfStackTemp)
thread->captureRegisters()
return
* any changes here will not affect GC
Now, when another thread performs a collection, the GC-free thread can be scanned from 8088 to 9976, regardless of any stuff happening lower down the stack.
Top of stack can be tricky to get right when a GUI framework does not really have a "main".
10000
-----------------------------------------------
9996 startup temp variable
9992 startup temp variable
-- main function --
setupWindows(onReadyCallback)......
...
8000
-- onReadyCallback --
7976 int t0
SetTopOfStack(&t0,false) -> 7966
__hxcpp_main();
setOnFrameCallack(haxeOnFrame)
return;
Later, the haxeOnFrame
callback is triggered, but not "below" __hxcpp_main
9800 -- haxeOnFrame ---
// Top of stack will be below bottom of stack.
Solutions:
- Make sure you get in at top of main
- may scan too much?
- Ratchet up top-of-stack in callbacks,
inForce = false
gc_set_top_of_stack(void * inTopOfStack,bool inForce);
- Detach main thread after
hxcpp_main
and reattach each callback- Android solution because render callbacks happen on different threads
gc_set_top_of_stack(&base,true);
- attachgc_set_top_of_stack(0,true);
- detach
- In debug mode,
hxcpp
will check for calls from unattached threads. hxcpp
can log conservative ranges. With a native debugger you can check the address of your local variables and ensure they are included.hxcpp
will scan native objects on the stack, but will not follow non-Haxe pointers to other objects, so additional GC roots may be required.
Cppia is a scriptable cpp subtarget for Haxe. A cppia script is an instructions assembly script that can be run inside a cppia host and gives you runtime speed at near-zero compilation time. It also lets you add performance critical code to the host, which gives you full cpp runtime speed for those parts.
To get started with Haxe/Java, create a new folder and save this class as Main.hx
.
To compile Haxe to Java we need two obvious prerequisites installed:
- hxjava haxelib. This is the support library for the Java backend of the Haxe compiler.
- JRE - Java Runtime Environment.
Run the following from the command line:
haxe --java bin --main Main
Another possibility is to create and run (double-click) a file called compile.hxml
. In this example the hxml-file should be in the same directory as the example class.
--java bin
--main Main
The compiler outputs in the given bin-folder, which contains the generated sources / .jar files which prints the traced message when you execute it.
To execute, run the following command:
java -jar bin/Main.jar
To get started with Haxe/JVM, create a new folder and save this class as Main.hx
.
To compile Haxe to JVM bytecode, you'll first need to install the hxjava haxelib. This is the Haxe Java support library, including build scripts and support code. Then run the following from the command line:
haxe --jvm bin/Main.jar --main Main
To save yourself some typing, you may create a .hxml
file (say, jvm.hxml
) in your project's top-level directory, containing:
--jvm bin/Main.jar
--main Main
and run it with the haxe
command:
haxe jvm.hxml
to produce the bin/Main.jar file.
To execute the jar file, you'll need to have a JRE (Java Runtime Environment) installed, such as OpenJDK or a commercially-supported JRE. Then run the jar file using the following command:
java -jar bin/Main.jar
Haxe can be used as a language for .NET platform through its C# target. Let's make a basic program using .NET Console class:
To compile Haxe to C# we need two obvious prerequisites installed:
- hxcs haxelib. This is the support library for the C# backend of the Haxe compiler.
- .NET development framework (either Microsoft.NET or Mono)
After that we can compile to C# using the --cs
option from either the command line or an hxml-file:
haxe --cs out --main Main
The compiler will output C# sources into out/src folder, then call C# compiler to build Main.exe file into out/bin folder.
By default, Haxe uses basic .NET 2.0 API provided by hxcs library (it ships mscorlib.dll and System.dll from the Mono project). We can specify different .NET version by providing -D net-ver=xx
define, where xx is major and minor digits of .NET version number, i.e. -D net-ver=40
for setting .NET version to 4.0. Note that currently, hxcs library only ships DLL files for .NET 2.0 and 4.0.
We can make Haxe use a custom set of DLL files as standard .NET framework. To do that, we need to first learn about how Haxe finds standard .NET libraries. Haxe/C# looks for .DLL files in a directory path, constructed from three components:
- .NET version (set by
-D net-ver=xx
, defaults to20
as described above) - .NET target (by default set to
net
, but could be changed using-D net-target=xxx
, wherexxx
could bemicro
,compact
or some other). - .NET std path (set by
--net-std
option, by default points to netlib directory inside hxcs library)
The resulting search path will be <net_std_path>/<net_target>-<net_ver>/
, taking in the consideration default values described above, without any specific configuration haxe will load all .NET DLL files found in <hxcs_install_path>/netlib/net-20/
.
Now if we provide the following options:
-D net-target=micro -D net-ver=35 --net-std=/dotnet
Haxe will load all .NET DLL files found in /dotnet/micro-35/
.
Haxe can directly load .NET assembly files (.DLL) and convert its type definitions for use as Haxe types. To load a .NET assembly, use --net-lib library.dll
compiler option. Haxe will then automatically parse types defined in that assembly file and make them available for import as Haxe types.
Some changes are performed to type naming of C# classes to make them fit into Haxe type system, namely:
- Namespaces are lowercased to follow Haxe package naming rules, so i.e.
UnityEngine
becomesunityengine
(note thatSystem
namespace is also prefixed withcs
, soSystem.Core
becomescs.system.core
) - Inner classes are generated as
OuterClassName_InnerClassName
and placed into theOuterClassName
module. So for example for an inner classB
inside a classA
inside a namespaceSomething
, the full Haxe type path will besomething.A.A_B
. Note however, that if you doimport something.A
, bothA
andA_B
class will be available within your module as per standard Haxe import mechanism. - Classes with type parameters have numbers of type params appended to their name, for example
Dictionary<K,V>
becomesDictionary_2<K,V>
Besides -D net-ver
and -D net-target
:
-D dll
compile to a .NET assembly instead of an executable file. Added automatically when no--main
is specified.-D real-position
don't generate #line directives that map C# expression positions to original .hx files. Useful for tracking down issues related to code generation.-D no-root
generate package-less haxe types in the haxe.root namespace to avoid conflicts with other types in the root namespace-D erase-generics
fully erase type parameters from generated C# files and generate non-generic classes. This is useful in some cases, like working with .NET Micro Framework or preventing generics-related issues with Unity3D AOT compiler.-D no-compilation
only generate C# sources and don't invoke C# compiler on them.-D keep-old-output
by default haxe cleans up stale generated source files from the output directory. This define disables that behaviour.-D dll-import
Haxe automatically adds NET_xx
defines where xx is major and minor version numbers .NET versions up to selected one. For example, when using .NET 4.0 (by providing -D net-ver=40
), we have the following defines set automatically: NET_20
, NET_21
, NET_30
, NET_35
and NET_40
. If we had -D net-ver=30
, we would only have NET_20
, NET_21
and NET_30
.
This is the list of C# specific metadata. For more information, see also the complete list of all Haxe built-in metadata.
Metadata | Usage | Description |
---|---|---|
@:nativeGen | on classes | don't generate reflection, generate proper type parameters. This is useful for some sort of interop, but slows down reflection and structural typing |
@:nativeGen | on "flat" enums | generate C# enum, but note that C# enums are not-nullable unlike haxe enums, so using null will be generated as a default enum value (0-indexed constructor). |
@:property | on non-physical fields (those with get/set/never accessors) | generate native C# properties. useful for implementing extern interfaces or providing API for use from C# |
@:event | on variables | generate an event delegate (this also requires pairing add_EventName, remove_EventName methods with relevant signatures |
@:protected | on a field | mark field as protected instead of public (could affect reflection, but useful for hiding fields when providing API for use from outside Haxe) |
@:struct | on classes | generate struct instead of class |
TODO: @:classCode
We can use @:functionCode
metadata for a method to generate raw C# code inside a method body. It completely replaces any haxe expressions in method body. For example:
@:functionCode("return (v is int);")
function isInt(v:Dynamic):Bool {
return false;
}
Which will generate:
public virtual bool isInt(object v) {
return (v is int);
}
Haxe supports compilation to Python 3.3+ code.
To get started with Haxe/Python, create a new folder and save this class as Main.hx
.
To compile, either run the following from the command line:
haxe --python hello.py --main Main
Another possibility is to create and run (double-click) a file called compile.hxml
. In this example the hxml file should be in the same directory as the example class.
--python hello.py
--main Main
The compiler outputs a file called hello.py
in the current folder, which can be executed with python hello.py
(or python3 hello.py
in case Python 2 is the default on the system) from the command line.
To get started with Haxe for Lua, it's necessary to pick a Lua version and install dependencies. All versions of Lua are supported, but may require different libraries. Lua 5.1, 5.2, 5.3, and LuaJIT 2.0 and 2.1 (beta) are supported.
Lua is a very lightweight language that ships with a much smaller feature set than Haxe. In some cases (e.g. regex), it's necessary to install supplementary libraries that are used to support basic Haxe functionality.
In order to cover all dependencies, it is recommended to install and use LuaRocks. However, if you do not utilize relevant behavior (e.g. regex) on a given platform, or if you are using an embedded Lua client, then it is not necessary to install any packages for basic language functionality.
With LuaRocks, install the following packages:
luarocks install lrexlib-pcre2
luarocks install luasocket
luarocks install luasec
luarocks install luv
luarocks install luautf8
luarocks install hx-lua-simdjson
On Lua 5.1 or Lua 5.4+, install the bit32 library:
luarocks install bit32
When developing for multiple Lua versions, it is recommended to use the Python package hererocks.
With Lua installed, it is possible to write a simple command line application.
Create a new folder, and save this class as Main.hx
.
class Main {
static function main() {
trace("hello world");
}
}
To compile, run the following:
haxe --lua out.lua --main Main
The extern functionality in Haxe provides a way of declaring type signatures for native Lua libraries.
The Lua target enables the following define flags for the Haxe complier:
-D lua-ver
Enable special features for a specific Lua version. Currently, this flag will enable extern methods that are specific to certain versions (e.g. table.pack in Lua > 5.2).-D luajit
Enable special features for LuaJIT. Currently this flag will enable thejit
andffi
module namespaces.-D lua-vanilla
Generate code lacking some functionality (e.g. UTF-8) but with no additional library dependencies.
Lua allows for multiple values to be returned from a given function. Haxe
does not support this by default, but can allow extern definitions to reference
multireturn values through the @:multiReturn
metadata.
class Main {
static function main() {
var strfind = NativeString.find("foobar", "bar");
trace(strfind.begin);
trace(strfind.end);
}
}
@:native("string")
extern class NativeString {
public static function find(str : String, target : String): StringFind;
}
@:multiReturn extern class StringFind {
var begin : Int;
var end : Int;
}
This example has three parts:
- The extern class
NativeString
which is an extern for the basestring
library in Lua. - The
StringFind
class which is marked as@:multiReturn
that describes the return values. - The Main class that invokes the string method as a basic example.
The multireturn behavior in Haxe is optimized based on usage. If fields are only accessed directly, the Haxe compiler will allocate the multireturn to individual variables. But, if you pass or assign the entire multireturn value, the compiler will wrap all values into a table object. This operation ensures that multireturn variable handling only carries as much overhead as needed.
Haxe supports compilation to HashLink bytecode or HashLink/C code. HashLink is a virtual machine designed for Haxe.
In order to run Hashlink bytecode and compile Hashlink/C, the Hashlink binaries must be installed. These binaries can be obtained either by downloading a release version or by building them from source.
Additionally, for compiling Haxe to Hashlink/C, the hashlink
haxelib package must also be installed. It is recommended to install it directly from source, via the command:
haxelib git hashlink https://github.com/HaxeFoundation/hashlink.git master other/haxelib/
To get started with Haxe/HashLink, create a new folder and save this class as Main.hx
.
To compile, either run the following from the command line:
haxe --hl hello.hl --main Main
Another possibility is to create and run (double-click) a file called compile.hxml
. In this example the hxml file should be in the same directory as the example class.
--hl hello.hl
--main Main
The compiler outputs a file called hello.hl
in the current folder, which can be executed with hl hello.hl
from the command line.
Haxe supports two modes of compilation for HashLink:
- Compilation to HashLink bytecode. This mode has a very fast compilation time, so it is suitable for daily development.
- Compilation to HashLink/C code, compiled with a native compiler to a regular executable. This mode results in the best performance, so it is suitable for final releases.
Compiling to HashLink bytecode is achieved by specifying a .hl
output in the --hl
argument:
haxe --main Main --hl output.hl
This produces a single output.hl
bytecode file, which can be executed with the HashLink JIT virtual machine with:
hl output.hl
Before compiling to Hashlink/C, the hashlink
haxelib library must be installed. See here for details.
Haxe code can then be compiled to HashLink/C code by specifying .c
output in the --hl
argument:
haxe --main Main --hl out/main.c
This produces files in the out
directory, which can be compiled to a native executable with a C compiler such as gcc
.
gcc -O3 -o hello -std=c++ -I out out/main.c -lhl [-L/path/to/required/hdll]
Note that during native compilation, the HL library must be linked. See here for information on obtaining Hashlink binaries.
On Windows, to make compilation easier, it is also possible to generate Visual Studio project files.
First, the HASHLINK
environment variable must be set up, pointing to the Hashlink install location. This is necessary as it is used within the project files to locate the Hashlink sources.
The project files can then be generated by adding the -D hlgen.makefile
define to the compilation command:
haxe --main Main --hl out/main.c -D hlgen.makefile=vs2019
This produces a main.sln
file in the out
directory, which can simply be opened in Visual Studio and built without any extra setup.