Contains Bazel rules for building and testing Haxe projects. This project is very much in progress and there's a good chance it won't work.
Based on the simple Go example here.
A shell environment on Windows is required, due to the use of run_shell
, which calls a bash environment. At this
point there isn't a better way to set the environment needed for Haxe to run properly. Either cygwin or minGW bash
(provided by Git for Windows) should work, but if you have one or both on your path it can cause problems. If needed,
set an explicit shell with the BAZEL_SH environment variable.
The haxelib directory is in the same directory as the downloaded Haxe distribution; this allows haxelibs to be reused
across builds within the same project. This also means that currently sandboxing is not supported - if every process
runs in its own sandbox, it won't have easy access to the common haxelibs. So for now the --spawn_strategy=local
parameter must be passed to bazel if sandboxing is supported on your platform.
In your WORKSPACE file, first specify the Haxe distribution to install with either haxe_download_windows_amd64
or a
specific version with haxe_download
. Next register the toolchain. So a minimal WORKSPACE file would look like this:
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "rules_haxe",
branch = "master",
remote = "https://github.com/kigero/rules_haxe",
)
load("@rules_haxe//:def.bzl", "haxe_download_windows_amd64")
haxe_download_windows_amd64(
name = "haxe_windows_amd64",
)
register_toolchains(
"@haxe_windows_amd64//:toolchain",
)
Note that an appropriate haxelib will automatically be added to the haxelibs list for the current target; for example if the target is "java", the "hxjava" haxelib will automatically be added.
load("@rules_haxe//:def.bzl", "haxe_library")
haxe_library(
name = "neko-lib",
srcs = glob(["src/main/haxe/**/*.hx"]),
debug = True,
library_name = "validation",
)
An alternative way of defining the library is to use the haxe_project_definition
rule:
load("@rules_haxe//:def.bzl", "haxe_library")
haxe_project_definition(
name = "haxe-def",
srcs = glob(["src/main/haxe/**/*.hx"]),
debug = True,
)
haxe_library(
name = "neko-lib",
library_name = "validation",
deps = ["//:haxe-def"],
)
The benefits to this route are:
- If you have rules to build libraries for multiple targets, you can define the project once and depend on it from those targets, allowing a common configuration to be used.
- If you have downstream projects that depend on this project, they can depend directly on the project definition rather than a build artifact for a specific target. This speeds up compilation of the downstream targets, and reduces the amount of boilerplate when those downstream projects build for multiple targets.
load("@rules_haxe//:def.bzl", "haxe_library")
haxe_executable(
name = "neko-lib",
srcs = glob(["src/main/haxe/**/*.hx"]),
debug = True,
executable_name = "validation",
main_class = "com.example.Main"
)
load("@rules_haxe//:def.bzl", "haxe_test")
haxe_test(
name = "neko-test",
srcs = glob(["src/test/haxe/**/*.hx"]),
haxelibs = {"hx3compat": "1.0.3"},
deps = ["//:neko-lib"],
)
Notice that only the test sources are included here; the library sources actually being tested are brought in through
the //:neko-lib
dependency. Alternatively you can include the library sources directly in the srcs
attribute.
This is handy for working within VSCode. It generates a build.hxml file based off the current configuration that works
with VSHaxe. The file is generated within the bazel-bin
directory; it's recommended to then link to this file from
the project directory with ln
or mklink
so that VSHaxe can find it easily.
load("@rules_haxe//:def.bzl", "haxe_library", "haxe_gen_hxml")
haxe_gen_hxml(
name = "gen-neko-hxml",
srcs = glob(["src/main/haxe/**/*.hx"]),
debug = True,
hxml_name = "build-neko.hxml",
library_name = "validation",
target = "neko",
visibility = ["//visibility:public"]
)
You have to provide the path to the bazel-myproject
symlink on the command line so that the proper relative paths from
the symlinked HXML file can be generated:
bazel build //:gen-neko-hxml --define=bazel_project_dir=%cd%/bazel-validation
Generates a standard library that can be used for downstream projects that may need Haxe code, especially if the
strip_haxe
parameter is being used when creating libraries. Currently this is only really tested for Java - it's
unknown yet whether other targets need this feature or not. The intended use case is where you're building a reusable
library in Haxe that gets included into a downstream jar; in that case you need the standard library to provide access
to the core Haxe stuff. The best practice is to use the common target from this package as it reduces compilation times
for multi-haxe project builds.
haxe_library(
name = "myjava-lib",
srcs = glob(["src/main/haxe/**/*.hx"]),
debug = True,
strip_haxe = True,
library_name = "myjava",
)
java_library(
name = "final-lib",
srcs = glob(["src/main/java/**/*.java"]),
deps = [
"//:myjava-lib",
"@rules_haxe//:std-java",
],
)
You can instantiate the rule locally as well if necessary.
When using a local toolchain (haxe_no_install
), this module will try to find the location of the Haxe installation
that contains the Haxe source code. It does this by running which haxe
, and if found, the directory containing the
haxe executable will be used to locate the source code. This directory can also be overridden by passing the
HAXE_HOME
environment variable via an action_env
parameter.
In some instances you need to compile a haxelib directly to a target language - similar to the standard lib above. That's what this rule is for. This configuration would compile the hx3compat lib to java.
haxe_haxelib_lib(
name = "hx3compat",
include = ["haxe.unit"],
haxelib = "hx3compat",
target = "java",
version = "1.0.4",
)
Dependent haxelibs can be a bit tricky at times.
- To avoid multiple installations of the same version of a haxelib, a locking mechanism is used. If a haxelib install
fails, the next time the process is run you will encounter a timeout period; after this period, the lock will be
cleared and the install can be tried again. The lock may also be manually cleared by removing the directory at
bazel-projectname/external/haxe_os_cpu/haxelib_install.sh.lib
. - If there are errors when changing versions, a
bazel clean
should handle the issue.
The targets that are currently actively supported are listed below; other targets may work.
- neko
- java
- php
- python
- cpp (at least on windows)
You can specify the target java version to compile to by adding a variable definition like this (for java 1.8):
--define=haxe_java_target_version=1.8
Setting this variable sets the -source
and -target
compiler options in the HXML file to the value set. This
propagates to dependencies as well, so if you usually compile for Java 11 but you have one pesky deployment target that
uses Java 1.8, setting this should compile all the dependencies with Java 1.8.
If not set, the default java toolchain will be used to get the source and target version.
On Windows (Linux has not been tested), getting the right MSVC environment can be... problematic. The HXCPP toolchain bat files that look for the various installations of MSVC tend to look in hardcoded paths, which can make it hard to pass in a variable that works for all situations.
- For MSVC 2015 and below, setting
HXCPP_MSVC
to the directory containing eithervsvars32.bat
orvcvars32.bat
in your environment, and then passing--action_env=HXCPP_MSVC
either on the command line or in a bazelrc file, should work for both 32 and 64 bit installs. - It looks like MSVC 2017 improved on this situation a bit by including a
vswhere
program, which HXCPP utilizes, to locate the installation root of Visual Studio. The problem with this is that the bat files use theProgramFiles(x86)
substitution, which seems to be not a real environment variable but something special done by the batch processor. This causes issues: HXCPP spawns a newcmd
shell to examine these variables, and for whatever reason this shell can't process that special variable. So with MSVC 2017, the capability is there in the bat files, but something with the nightmare that is cmd->bazel->bash->cmd the ability to use the right substitution to find that program is lost. As it turns out though, if that substitution can be made, HXCPP seems to work properly. So... the horrible solution that is currently implemented in this project: if the HXCPP haxelib is installed, any bat files in the haxelib's toolchain directory relating to finding MSVC variables are postprocessed to remove the substitution and instead set the default path to this folder. See thetemplates/postprocess_hxcpp.sh
file for the exact command. Yes, editing files after the fact is terrible, but at this time I don't have a better solution. The only advantage is that you shouldn't need to pass any special CLI parameters to locate the MSVC installation. If/when this causes a problem, I'll revisit it then.
A CPP compiled project can be used as a dependency for a downstream cc_library
or cc_binary
rule. This support is
very basic at this time, but generally it should be available - at least on windows with MSVC. Of course there are some
caveats:
- To build a static library, include
extra_args = ["-D static_link"]
in thehaxe_library
rule. This generates a .lib output. - To build a dynamic library, include
extra_args = ["-D dll_export", "-D dll_link"]
in thehaxe_library
rule. This generates a .dll and a .lib output. - The response includes a
CcInfo
provider with the compilation and linking context. Currently this is a bit messy.- Headers must be File objects that end in an appropriate extension (e.g.
.h
). Directories of results, which is what we have as the exact set of headers isn't known until Haxe compiles the code, are allowed as long as they have the right extension. So the compilation context copies the includes to a separate folder ending in.h
. - The linking context includes the static or dynamic libraries based on the Haxe defines.
- Headers must be File objects that end in an appropriate extension (e.g.
The resultant rule can be used like this:
haxe_library(
name = "mylib",
extra_args = [
"-D dll_export",
"-D dll_link",
],
target = "cpp",
deps = ["//:haxe-def"],
)
cc_binary(
name = "Project",
srcs = ["//:sources"],
deps = [":mylib"],
)
In the source code, include the following:
#include <HxcppConfig-19.h>
extern "C"
{
extern const char *hxRunLibrary();
}
...
void MyClass::myInitFunc() {
// Initialize haxe.
hxRunLibrary();
}
Windows is a bit of a pain; you'll need symlink support as described in the Bazel docs, as well as some variables passed through from the shell. At a minimum your .bazelrc or command line flags should have the following:
startup --windows_enable_symlinks
build --enable_runfiles --action_env=ComSpec --action_env=USERPROFILE
test --action_env=ComSpec --action_env=USERPROFILE
Unfortunately the variable specified in --action_env
is case sensitive; if you have a few different environments that provide an environment variable in different cases (e.g. CMD vs Cygin) it appears you can just pass the parameter twice in the .bazelrc. If you need to use a proxy, you should also add --action_env=HTTP_PROXY --action_env=HTTPS_PROXY
and set those variables accordingly.
Documentation can be generated by running bazel build //docs:docs
.
Unit tests of the rules can be executed with bazel test //test:test
, possibly with the --cache_test_results=no
option to force tests to be run.