#!/usr/bin/env bash # # @license Apache-2.0 # # Copyright (c) 2017 The Stdlib Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Script for compiling WebAssembly for a provided absolute package path. # # Usage: compile_wasm # # Arguments: # # pkg Package directory (absolute path). # # # Environment Variables: # # NODE Command for running Node.js. Default: `node`. # NODE_PATH Path for resolving node modules. # CLANG_COMPILER LLVM Clang compiler. Default: `clang`. # CLANG_SYSROOT Path to a C standard library. # EMCC_COMPILER Emscripten C compiler. Default: `emcc`. # WASM2WAT Command for converting WebAssembly binary format to text format. Default. `wasm2wat`. # WASM2JS Command for converting WebAssembly binary format to JavaScript. Default. `wasm2js`. # SRC_FOLDER Folder containing source files. Default: `src`. # INCLUDE Includes (e.g., `-I /foo/bar -I /a/b`). # SOURCE_FILES Source file list. # LIBRARIES Linked libraries (e.g., `-lopenblas -lpthreads`). # LIBPATH Library paths (e.g., `-L /foo/bar -L /a/b`). # # shellcheck disable=SC2181,SC2153 # VARIABLES # # Set the package path: pkg_path="$1" # Define the command for running Node.js: node_cmd="${NODE}" if [[ -z "${node_cmd}" ]]; then node_cmd='node' fi # Define the node path: node_path="${NODE_PATH}" # Define the path to the LLVM `clang` compiler: clang_compiler="${CLANG_COMPILER}" if [[ -z "${clang_compiler}" ]]; then clang_compiler='clang' fi # Define the path to the Emscripten `emcc` compiler: emcc_compiler="${EMCC_COMPILER}" if [[ -z "${emcc_compiler}" ]]; then emcc_compiler='emcc' fi # Define the path to an executable for converting a WebAssembly binary to the WebAssembly text format: wasm_to_wat="${WASM2WAT}" if [[ -z "${wasm_to_wat}" ]]; then wasm_to_wat='wasm2wat' fi # Define the path to an executable for converting a WebAssembly binary to JavaScript: wasm_to_js="${WASM2JS}" if [[ -z "${wasm_to_js}" ]]; then wasm_to_js='wasm2js' fi # Define the folder containing source files: src_folder="${SRC_FOLDER}" if [[ -z "${src_folder}" ]]; then src_folder='src' fi # Define a path to a C standard library: clang_sysroot="${CLANG_SYSROOT}" # Define a list of external `include` directories: include="${INCLUDE}" # Define a list of external source files: source_files="${SOURCE_FILES}" # Define a list of libraries (e.g., `-lopenblas -lpthreads`): libraries="${LIBRARIES}" # Define a list of library paths (e.g., `-L /foo/bar -L /beep/boop`): libpath="${LIBPATH}" # FUNCTIONS # # Defines an error handler. # # $1 - error status on_error() { echo 'ERROR: An error was encountered during execution.' >&2 cleanup exit "$1" } # Runs clean-up tasks. cleanup() { echo '' >&2 } # Prints a success message. print_success() { echo 'Success!' >&2 } # Prints usage information. usage() { echo '' >&2 echo 'Usage: compile_wasm ' >&2 echo '' >&2 } # Resolves external `include` directories. # # $1 - package directory resolve_includes() { local includes local script local opts opts="{'wasm':true,'os':''}" # Generate the script for resolving external include directories: script='"'"var path = require('path'); var arr = require('@stdlib/utils/library-manifest')(path.join('$1','manifest.json'),${opts},{'basedir':'$1','paths':'posix'}).include; var str = ''; for (var i = 0; i < arr.length; i++){var p = path.resolve('$1', arr[i]); if (p.indexOf('$1') === 0) {continue;}; str += '-I '+p+' ';}; console.log(str.substring(0, str.length-1));"'"' # Resolve include directories: includes=$(eval NODE_PATH="${node_path}" "${node_cmd}" -e "${script}") echo "${includes}" } # Resolves external source files. # # $1 - package directory resolve_source_files() { local source_files local script local opts opts="{'wasm':true,'os':''}" # Generate the script for resolving external source files: script='"'"var path = require('path'); var arr = require('@stdlib/utils/library-manifest')(path.join('$1','manifest.json'),${opts},{'basedir':'$1','paths':'posix'}).src; var str = ''; for (var i = 0; i < arr.length; i++){var p = path.resolve('$1', arr[i]); if (p.indexOf('$1') === 0) {continue;}; str += p+' ';}; console.log(str.substring(0, str.length-1));"'"' # Resolve files: source_files=$(eval NODE_PATH="${node_path}" "${node_cmd}" -e "${script}") echo "${source_files}" } # Resolves libraries. # # $1 - package directory resolve_libraries() { local libraries local script local opts opts="{'wasm':true,'os':''}" # Generate the script for resolving libraries: script='"'"var path = require('path'); var arr = require('@stdlib/utils/library-manifest')(path.join('$1','manifest.json'),${opts},{'basedir':'$1','paths':'posix'}).libraries; var str = ''; for (var i = 0; i < arr.length; i++){str += arr[i]+' ';}; console.log(str.substring(0, str.length-1));"'"' # Resolve libraries: libraries=$(eval NODE_PATH="${node_path}" "${node_cmd}" -e "${script}") echo "${libraries}" } # Resolves library paths. # # $1 - package directory resolve_libpaths() { local libpath local script local opts opts="{'wasm':true,'os':''}" # Generate the script for resolving library paths: script='"'"var path = require('path'); var arr = require('@stdlib/utils/library-manifest')(path.join('$1','manifest.json'),${opts},{'basedir':'$1','paths':'posix'}).libpath; var str = ''; for (var i = 0; i < arr.length; i++){var p = path.resolve('$1', arr[i]); str += '-L '+p+' ';}; console.log(str.substring(0, str.length-1));"'"' # Resolve library paths: libpath=$(eval NODE_PATH="${node_path}" "${node_cmd}" -e "${script}") echo "${libpath}" } # Compiles WebAssembly. # # $1 - source directory compile() { cd "$1" && CLANG_COMPILER="${clang_compiler}" CLANG_SYSROOT="${clang_sysroot}" EMCC_COMPILER="${emcc_compiler}" WASM2WAT="${wasm_to_wat}" WASM2JS="${wasm_to_js}" INCLUDE="${include}" SOURCE_FILES="${source_files}" LIBRARIES="${libraries}" LIBPATH="${libpath}" make wasm 2>&1 if [[ "$?" -ne 0 ]]; then echo 'Error when attempting to compile WebAssembly.' >&2 return 1 fi echo 'Successfully compiled WebAssembly.' >&2 return 0 } # MAIN # # Main execution sequence. main() { local src_dir="${pkg_path}/${src_folder}" if [[ -z "${include}" ]]; then echo 'Resolving external include directories...' >&2 include=$(resolve_includes "${pkg_path}") if [[ "$?" -ne 0 ]]; then on_error 1 fi fi if [[ -z "${source_files}" ]]; then echo 'Resolving external source files...' >&2 source_files=$(resolve_source_files "${pkg_path}") if [[ "$?" -ne 0 ]]; then on_error 1 fi fi if [[ -z "${libraries}" ]]; then echo 'Resolving libraries...' >&2 libraries=$(resolve_libraries "${pkg_path}") if [[ "$?" -ne 0 ]]; then on_error 1 fi fi if [[ -z "${libpath}" ]]; then echo 'Resolving library paths...' >&2 libpath=$(resolve_libpaths "${pkg_path}") if [[ "$?" -ne 0 ]]; then on_error 1 fi fi echo 'Compiling WebAssembly...' >&2 compile "${src_dir}" if [[ "$?" -ne 0 ]]; then on_error 1 fi print_success cleanup exit 0 } # Handle arguments... if [[ "$#" -eq 0 ]]; then usage exit 0 elif [[ "$#" -gt 1 ]]; then echo 'ERROR: unrecognized arguments. Must provide a package path.' >&2 exit 1 fi # Run main: main