Skip to content

Commit

Permalink
build-system unit-testing & other extras
Browse files Browse the repository at this point in the history
- fix install.alpine.packages.sh chmod +x
- refactor nodejs.py sub-commands (git-CLI style)
- add j2v8-cli convenience scripts with command aliases (for win32/linux/macos)
- do not run NodeJsTest when native lib was not compiled with node features included
- add check in CMake node js lib scripts if all njs lib files exist, exit with error if any is missing
- add node 7.9.0 patch file (works for linux, but not windows currently)
- j2v8jni build-step is now called j2v8cpp
- j2v8jni is a new build-step using javah to regenerate J2V8 JNI header files
- j2v8jni build-step is skipped if the required V8.class file does not yet exist
- implement basic unit-testing for build-system
- centralize maven build-steps logic and also just run maven pre-build actions once per build-process
  (e.g. copy native J2V8 libs to the Java directories)
  • Loading branch information
drywolf authored and irbull committed Aug 29, 2017
1 parent 9a5735d commit 2124ad1
Show file tree
Hide file tree
Showing 54 changed files with 1,395 additions and 243 deletions.
27 changes: 22 additions & 5 deletions BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ Override build-steps ? (leave empty to run pre-configured steps): j2v8

The J2V8 build-system performs several build steps in a fixed order to produce the final J2V8 packages for usage on the designated target platforms. What follows is a short summary for what each of the executed build-steps does and what output artifacts are produced by each step.

```
Node.js --> CMake --> JNI --> C++ --> Optimize --> Java/Android --> JUnit
```
---
## Node.js

Expand All @@ -56,9 +59,10 @@ __Inputs:__
__Artifacts:__
- Node.js & V8 static link libraries
- `./node/out/`
- `./node/build/`
- `./node/Debug/`
- `./node/Release/`
- *win32 specific*
- `./node/build/`
- `./node/Debug/`
- `./node/Release/`
---
## CMake

Expand All @@ -75,12 +79,25 @@ __Artifacts:__
- CMake generated Makefiles / IDE Project-files
- `./cmake.out/{platform}.{architecture}/`
---
## JNI
## JNI Header Generation

Generate the JNI glue header file from the native method definitions of the Java `V8` class.

__Inputs__:
- Java V8.class file
- `./target/classes/com/eclipsesource/v8/V8.class`

__Artifacts:__
- J2V8 C++ JNI header file
- `./jni/com_eclipsesource_v8_V8Impl.h`
---
## C++

Compile and link the J2V8 C++ shared libraries (.so/.dylib/.dll), which provide the JNI bridge to interop with the C++ code of Node.js / V8.
Compile and link the J2V8 native shared libraries (.so/.dylib/.dll), which contain the C++ JNI bridge code to interop with the embedded Node.js / V8 parts.

__Inputs__:
- CMake generated Makefiles / IDE Project-files
- Node.js / V8 static link libraries & C++ header files
- J2V8 C++ JNI source code
- `./jni/com_eclipsesource_v8_V8Impl.h`
- `./jni/com_eclipsesource_v8_V8Impl.cpp`
Expand Down
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ set(J2V8_JDK_DIR ${Java_ROOT} CACHE STRING "Path to the Java JDK dependency")
set(J2V8_NODEJS_DIR "${CMAKE_SOURCE_DIR}/node" CACHE STRING "Path to the Node.js dependency")

# get the required Node.js link libraries
get_njs_libs(${J2V8_NODEJS_DIR} "Debug")
get_njs_libs(${J2V8_NODEJS_DIR} "Release")
get_njs_libs(${J2V8_NODEJS_DIR} "Debug" FALSE)
get_njs_libs(${J2V8_NODEJS_DIR} "Release" TRUE)

# j2v8 build options
set(J2V8_TARGET_ARCH "" CACHE STRING "The target architecture for the build.")
Expand Down
1 change: 1 addition & 0 deletions build_system/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test-reports
3 changes: 2 additions & 1 deletion build_system/build_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
CLIStep(c.build_node_js, " Builds the Node.js & V8 dependency artifacts that are later linked into the J2V8 native bridge code.\n" +
" (only works if the Node.js source was checked out into the J2V8 ./node directory)"),
CLIStep(c.build_j2v8_cmake, " Uses CMake to generate the native Makefiles / IDE project files to later build the J2V8 C++ native bridge shared libraries."),
CLIStep(c.build_j2v8_jni, " Compile and link the J2V8 C++ shared libraries (.so/.dylib/.dll), which provide the JNI bridge to interop with the C++ code of Node.js / V8."),
CLIStep(c.build_j2v8_jni, " Generate the J2V8 JNI C++ Header files."),
CLIStep(c.build_j2v8_cpp, " Compile and link the J2V8 C++ shared libraries (.so/.dylib/.dll), which provide the JNI bridge to interop with the C++ code of Node.js / V8."),
CLIStep(c.build_j2v8_optimize, " The native J2V8 libraries are optimized for performance and/or filesize by using the available tools of the target-platform / compiler-toolchain."),
CLIStep(c.build_j2v8_java, " Compiles the Java source code and packages it, including the previously built native libraries, into the final package artifacts.\n" +
" For the execution of this build-step Maven (Java) or Gradle (Android) are used for the respective target platforms."),
Expand Down
65 changes: 38 additions & 27 deletions build_system/build_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
from shell_build import ShellBuildSystem
import immutable

# collection of all parsed build-steps that will then be passed on to the core build function
# (this list must only contain atomic steps after all step evaluations are finished)
parsed_steps = set()
class BuildState:
# collection of all parsed build-steps that will then be passed on to the core build function
# (this list must only contain atomic steps after all step evaluations are finished)
parsed_steps = set()

# a registry/dictionary of evaluation-functions that translate from their corresponding step/alias
# into the list of atomic build-steps (see parsed_steps above)
step_evaluators = {}
# a registry/dictionary of evaluation-functions that translate from their corresponding step/alias
# into the list of atomic build-steps (see parsed_steps above)
step_evaluators = {}

#-----------------------------------------------------------------------
# Advanced build-step parsing (anti-steps, multi-steps)
Expand All @@ -28,11 +29,14 @@ def atomic_step(step, alias = None):
if (alias is None):
alias = step

step_eval = BuildState.step_evaluators
parsed_steps = BuildState.parsed_steps

# add step handler (step => step)
step_evaluators[alias] = lambda: parsed_steps.add(step)
step_eval[alias] = lambda: parsed_steps.add(step)

# add anti-step handler (step => ~step)
step_evaluators["~" + alias] = lambda: parsed_steps.discard(step)
step_eval["~" + alias] = lambda: parsed_steps.discard(step)

# register additional anti-step in CLI
bc.avail_build_steps.append("~" + alias)
Expand All @@ -43,15 +47,18 @@ def multi_step(alias, include, exclude = []):
the defined step alias name was detected. Also the inverted anti-steps sequence
will be evaluated if the "~" prefixed alias is recognized.
"""

step_eval = BuildState.step_evaluators

# add aliased step-sequence (alias => step1, step2, ... , stepN)
step_evaluators[alias] = lambda: \
[step_evaluators.get(s)() for s in include] + \
[step_evaluators.get("~" + s)() for s in exclude]
step_eval[alias] = lambda: \
[step_eval.get(s)() for s in include] + \
[step_eval.get("~" + s)() for s in exclude]

# add aliased anti-step-sequence (~alias => ~step1, ~step2, ... , ~stepN)
step_evaluators["~" + alias] = lambda: \
[step_evaluators.get("~" + s)() for s in include] + \
[step_evaluators.get(s)() for s in exclude]
step_eval["~" + alias] = lambda: \
[step_eval.get("~" + s)() for s in include] + \
[step_eval.get(s)() for s in exclude]

# register additional anti-step in CLI
bc.avail_build_steps.append("~" + alias)
Expand All @@ -74,6 +81,7 @@ def init_buildsteps():
c.build_node_js,
c.build_j2v8_cmake,
c.build_j2v8_jni,
c.build_j2v8_cpp,
c.build_j2v8_optimize,
])

Expand All @@ -83,11 +91,11 @@ def init_buildsteps():

def evaluate_build_step_option(step):
"""Find the registered evaluator function for the given step and execute it"""
step_eval_func = step_evaluators.get(step, raise_unhandled_option(step))
step_eval_func = BuildState.step_evaluators.get(step, raise_unhandled_option(step))
step_eval_func()

def raise_unhandled_option(step):
return lambda: sys.exit("INTERNAL-ERROR: Tried to handle unrecognized build-step \"" + step + "\"")
return lambda: utils.cli_exit("INTERNAL-ERROR: Tried to handle unrecognized build-step \"" + step + "\"")

# initialize the advanced parsing evaluation handlers for the build.py CLI
init_buildsteps()
Expand Down Expand Up @@ -115,35 +123,38 @@ def execute_build(params):
if (isinstance(params, dict)):
params = cli.BuildParams(params)

# can be used to force output of all started sub-processes through the host-process stdout
utils.redirect_stdout_enabled = hasattr(params, "redirect_stdout") and params.redirect_stdout

if (params.target is None):
sys.exit("ERROR: No target platform specified")
utils.cli_exit("ERROR: No target platform specified")

if (params.docker and params.vagrant):
sys.exit("ERROR: Choose either Docker or Vagrant for the build, can not use both")
utils.cli_exit("ERROR: Choose either Docker or Vagrant for the build, can not use both")

target = params.target

if (not target in bc.platform_configs):
sys.exit("ERROR: Unrecognized target platform: " + target)
utils.cli_exit("ERROR: Unrecognized target platform: " + target)

# this defines the PlatformConfig / operating system the build should be run for
target_platform = bc.platform_configs.get(target)

if (params.arch is None):
sys.exit("ERROR: No target architecture specified")
utils.cli_exit("ERROR: No target architecture specified")

avail_architectures = target_platform.architectures

if (not params.arch in avail_architectures):
sys.exit("ERROR: Unsupported architecture: \"" + params.arch + "\" for selected target platform: " + target)
utils.cli_exit("ERROR: Unsupported architecture: \"" + params.arch + "\" for selected target platform: " + target)

if (params.buildsteps is None):
sys.exit("ERROR: No build-step specified, valid values are: " + ", ".join(bc.avail_build_steps))
utils.cli_exit("ERROR: No build-step specified, valid values are: " + ", ".join(bc.avail_build_steps))

if (not params.buildsteps is None and not isinstance(params.buildsteps, list)):
params.buildsteps = [params.buildsteps]

global parsed_steps
parsed_steps = BuildState.parsed_steps
parsed_steps.clear()

# go through the raw list of build-steps (given by the CLI or an API call)
Expand All @@ -155,7 +166,7 @@ def execute_build(params):
parsed_steps = [step for step in bc.atomic_build_step_sequence if step in parsed_steps]

if (len(parsed_steps) == 0):
sys.exit("WARNING: No build-steps to be done ... exiting")
utils.cli_exit("WARNING: No build-steps to be done ... exiting")

build_cwd = utils.get_cwd()

Expand All @@ -168,7 +179,7 @@ def execute_build(params):
# try to find the configuration parameters to run the cross-compiler
if (cross_sys):
if (cross_configs.get(cross_sys) is None):
sys.exit("ERROR: target '" + target + "' does not have a recognized cross-compile host: '" + cross_sys + "'")
utils.cli_exit("ERROR: target '" + target + "' does not have a recognized cross-compile host: '" + cross_sys + "'")
else:
cross_cfg = cross_configs.get(cross_sys)

Expand Down Expand Up @@ -231,14 +242,14 @@ def execute_build_step(build_system, build_step):
cross_cfg = cross_configs.get(params.cross_agent)

if (cross_cfg is None):
sys.exit("ERROR: internal error while looking for cross-compiler config: " + params.cross_agent)
utils.cli_exit("ERROR: internal error while looking for cross-compiler config: " + params.cross_agent)

build_cwd = cross_cfg.build_cwd

# execute all steps from a list that parsed / evaluated before (see the "build-step parsing" section above)
for step in parsed_steps:
if (not step in build_steps):
print("INFO: skipping build step \"" + step + "\" (not configured and/or supported for platform \"" + params.target + "\")")
print("WARNING: skipping build step \"" + step + "\" (not configured and/or supported for platform \"" + params.target + "\")")
continue

target_step = build_steps[step]
Expand Down
3 changes: 2 additions & 1 deletion build_system/build_interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import build_configs as bcfg
import build_executor as bex
import build_utils as utils

def run_interactive_cli():
idx = 0
Expand All @@ -20,7 +21,7 @@ def run_interactive_cli():
else input("Select a predefined build-configuration to run: ")

if not isinstance(sel_index, int) or sel_index < 0 or sel_index > len(bcfg.configs):
sys.exit("ERROR: Must enter a valid test index in the range [0 ... " + str(len(bcfg.configs)) + "]")
utils.cli_exit("ERROR: Must enter a valid test index in the range [0 ... " + str(len(bcfg.configs)) + "]")

sel_cfg = bcfg.configs[sel_index]

Expand Down
2 changes: 1 addition & 1 deletion build_system/build_structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def cross_compiler(self, cross_host_name):
compiler = self.cross_compilers.get(cross_host_name)

if (not compiler):
sys.exit("ERROR: internal error while looking for cross-compiler: " + cross_host_name)
utils.cli_exit("ERROR: internal error while looking for cross-compiler: " + cross_host_name)

return compiler()

Expand Down
Loading

0 comments on commit 2124ad1

Please sign in to comment.