Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial toolchain support #827

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
4de87f2
feat(builtin): add nodejs toolchain support
Globegitter Jun 13, 2019
6c7d1b7
docs(builtin): add some docs about the toolchain
Globegitter Jun 13, 2019
eb4b8d3
docs(builtin): add a note about native dependencies
Globegitter Jun 14, 2019
65434c3
test(builtin): add basic nodejs toolchain unit test
Globegitter Jun 19, 2019
0f454bd
fix(builtin): run buildifier
Globegitter Jun 19, 2019
c240a4d
fix(builtin): run buildifier
Globegitter Jun 19, 2019
7b94605
ci(builtin): add debug statement for ci
Globegitter Jun 19, 2019
d969c85
fix(builtin): fix windows check
Globegitter Jun 19, 2019
7b05be9
fix(builtin): is_windows checks now take repository naming into account
Globegitter Jun 19, 2019
d9973d7
fix(builtin): buildifier
Globegitter Jun 19, 2019
eb1e527
fix(builtin): fix exported binary on windows
Globegitter Jun 19, 2019
e5bdfff
fix(builtin): fix note path for windows
Globegitter Jun 19, 2019
e04e3f3
fix(builtin): fix buildifier
Globegitter Jun 19, 2019
1912b1f
docs(builtin): add comment
Globegitter Jun 19, 2019
b2322ba
fix(builtin): handle vendored node case properly
Globegitter Jun 19, 2019
794eaee
style(builtin): buildifier
Globegitter Jun 19, 2019
9f1b1e5
fix(builtin): fix vendored node check
Globegitter Jun 19, 2019
97b8daa
test(builtin): extend toolchain test
Globegitter Jun 19, 2019
053609a
fix(builtin): changes on review
gregmagolan Jun 27, 2019
d4b55c6
fix(builtin): improve docs and address comments
Globegitter Jul 3, 2019
fecd7e3
docs(builtin): add note about rbe
Globegitter Jul 3, 2019
73cb86d
fix(builtin): fix vendored node case
Globegitter Jul 3, 2019
77a6f4b
fix(builtin): js template substituion
Globegitter Jul 3, 2019
40b3751
refactor(builtin): create the convenience nodejs repo as a pure alias…
Globegitter Jul 3, 2019
62a9a2f
fix(builtin): add missing file
Globegitter Jul 3, 2019
15583f1
style(builtin): lint
Globegitter Jul 3, 2019
af5eed6
fix(builtin): another vendored node fix
Globegitter Jul 3, 2019
faf3174
fix(builtin): ensure public visibility
Globegitter Jul 3, 2019
5972e07
fix(builtin): fix starlark tests
Globegitter Jul 3, 2019
0fab68c
style(builtin): npm packager
Globegitter Jul 3, 2019
927c336
style(builtin): buildifier
Globegitter Jul 3, 2019
6200ccb
fix(builtin): export build template
Globegitter Jul 3, 2019
1beeac7
fix(builtin): ensure we include build template in package content
Globegitter Jul 3, 2019
4e5566d
style(builtin): buildifier
Globegitter Jul 3, 2019
c213936
fix(builtin): templating substitution issue
Globegitter Jul 3, 2019
0845f9c
fix(builtin): make sure to also strip out path separator
Globegitter Jul 3, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ bzl_library(
"//internal/jasmine_node_test:bzl",
"//internal/npm_package:bzl",
"//internal/rollup:bzl",
"//toolchains/node:bzl",
],
)

Expand Down Expand Up @@ -68,6 +69,7 @@ npm_package(
"//internal/npm_install:package_contents",
"//internal/npm_package:package_contents",
"//internal/web_package:package_contents",
"//toolchains/node:package_contents",
],
)

Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,24 @@ Note: the arguments passed to `bazel run` after `--` are forwarded to the execut

[bazel instructions]: https://docs.bazel.build/versions/master/install.html

### Toolchains

When you add `node_repositories()` to your `WORKSPACE` file it will setup a node toolchain for all currently supported platforms, Linux, macOS and Windows. Amongst other things this adds support for cross-compilations as well as Remote Build Execution support. For more detailed information also see [Bazel Toolchains](https://docs.bazel.build/versions/master/toolchains.html).

If you have an advanced use-case you can also register your own toolchains and call `node_configure` directly to manually setup a toolchain.

#### Cross-compilation

Toolchains allow us to support cross-compilation, e.g. building a linux binary from mac or windows. To tell Bazel to provide a toolchain for a different platform you have to pass in the `--platforms` flag. Currently supported values are:

- `@build_bazel_rules_nodejs//toolchains/node:linux_amd64`
- `@build_bazel_rules_nodejs//toolchains/node:darwin_amd64`
- `@build_bazel_rules_nodejs//toolchains/node:windows_amd64`

So if for example you want to build a docker image from a non-linux platform you would run `bazel build --platforms=@build_bazel_rules_nodejs//toolchains/node:linux_amd64 //app`, which will ensure that the linux nodejs binary is downloaded and provided to the nodejs_binary target.

Note: The toolchain currently only provides a platform-specific nodejs binary. Any native modules will still be fetched/built, by npm/yarn, for your host platform, so they will not work on the target platform. Support for cross-compilation with native dependencies will follow.
gregmagolan marked this conversation as resolved.
Show resolved Hide resolved

## Usage

### Running a program from npm
Expand Down
17 changes: 14 additions & 3 deletions internal/common/os_name.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@
"""Helper function for repository rules
"""

OS_ARCH_NAMES = [
("darwin", "amd64"),
("windows", "amd64"),
("linux", "amd64"),
]

OS_NAMES = ["_".join(os_arch_name) for os_arch_name in OS_ARCH_NAMES]

def is_windows(os_name):
return os_name == OS_NAMES[1]

def os_name(repository_ctx):
"""Get the os name for a repository rule

Expand All @@ -26,10 +37,10 @@ def os_name(repository_ctx):
"""
os_name = repository_ctx.os.name.lower()
if os_name.startswith("mac os"):
return "darwin_amd64"
return OS_NAMES[0]
elif os_name.find("windows") != -1:
return "windows_amd64"
return OS_NAMES[1]
elif os_name.startswith("linux"):
return "linux_amd64"
return OS_NAMES[2]
else:
fail("Unsupported operating system: " + os_name)
gregmagolan marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 5 additions & 1 deletion internal/node/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ exports_files([
"node_repositories.bzl", # Exported to be consumed for generating skydoc.
"node_launcher.sh",
"node_loader.js",
"BUILD.nodejs_host_os_alias.tpl",
])

filegroup(
Expand All @@ -39,6 +40,9 @@ filegroup(
"*.bzl",
"*.js",
"*.sh",
]) + ["BUILD.bazel"],
]) + [
"BUILD.bazel",
"BUILD.nodejs_host_os_alias.tpl",
],
visibility = ["//:__pkg__"],
)
27 changes: 27 additions & 0 deletions internal/node/BUILD.nodejs_host_os_alias.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by node_repositories.bzl
package(default_visibility = ["//visibility:public"])

# aliases for exports_files
alias(name = "run_npm.sh.template", actual = "TEMPLATE_run_npm")
alias(name = "bin/node_repo_args.sh", actual = "TEMPLATE_node_repo_args")
# windows specific aliases
alias(name = "bin/nodejs/node.exe", actual = "TEMPLATE_actual_node_bin")
alias(name = "bin/node.cmd", actual = "TEMPLATE_wrapped_node_bin")
alias(name = "bin/npm.cmd", actual = "TEMPLATE_npm")
alias(name = "bin/npm_node_repositories.cmd", actual = "TEMPLATE__npm_node_repositories")
alias(name = "bin/yarn.cmd", actual = "TEMPLATE_yarn")
alias(name = "bin/yarn_node_repositories.cmd", actual = "TEMPLATE__yarn_node_repositories")
# linux/mac specific aliases
alias(name = "bin/nodejs/bin/node", actual = "TEMPLATE_actual_node_bin")
alias(name = "bin/node", actual = "TEMPLATE_wrapped_node_bin")
alias(name = "bin/npm", actual = "TEMPLATE_npm")
alias(name = "bin/npm_node_repositories", actual = "TEMPLATE__npm_node_repositories")
alias(name = "bin/yarn", actual = "TEMPLATE_yarn")
alias(name = "bin/yarn_node_repositories", actual = "TEMPLATE__yarn_node_repositories")


# aliases for other aliases
alias(name = "node_bin", actual = "TEMPLATE_actual_node_bin")
alias(name = "node", actual = "TEMPLATE_wrapped_node_bin")
alias(name = "npm", actual = "TEMPLATE__npm_node_repositories")
alias(name = "yarn", actual = "TEMPLATE__yarn_node_repositories")
6 changes: 3 additions & 3 deletions internal/node/generate_build_file.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const fs = require('fs');
const path = require('path');

const IS_WINDOWS = TEMPLATED_is_windows;
const NODE_DIR = 'TEMPLATED_node_dir';
const IS_VENDORED_NODE = TEMPLATED_vendored_node;
const NODE_ACTUAL = 'TEMPLATED_node_actual';
const NODE_BIN_ACTUAL = 'TEMPLATED_node_bin_actual';
const NPM_ACTUAL = 'TEMPLATED_npm_actual';
Expand Down Expand Up @@ -65,12 +65,12 @@ module.exports = { main };

function generateBuildFile() {
const binaryExt = IS_WINDOWS ? '.cmd' : '';
const exportedNodeBin = IS_VENDORED_NODE ? '' : `\n "${NODE_BIN_ACTUAL}",`;
const buildFile = `# Generated by node_repositories.bzl
package(default_visibility = ["//visibility:public"])
exports_files([
"run_npm.sh.template",
"bin/node_repo_args.sh",
"${NODE_DIR}/bin/node",
"bin/node_repo_args.sh",${exportedNodeBin}
"bin/node${binaryExt}",
"bin/npm${binaryExt}",
"bin/npm_node_repositories${binaryExt}",
Expand Down
60 changes: 34 additions & 26 deletions internal/node/node.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ def _short_path_to_manifest_path(ctx, short_path):
return ctx.workspace_name + "/" + short_path

def _nodejs_binary_impl(ctx):
node = ctx.file.node
node_modules = depset(ctx.files.node_modules)

# Also include files from npm fine grained deps as inputs.
Expand Down Expand Up @@ -164,25 +163,38 @@ def _nodejs_binary_impl(ctx):
if hasattr(ctx.attr, "expected_exit_code"):
expected_exit_code = ctx.attr.expected_exit_code

substitutions = {
"TEMPLATED_args": " ".join([
expand_location_into_runfiles(ctx, a)
for a in ctx.attr.templated_args
]),
"TEMPLATED_env_vars": env_vars,
"TEMPLATED_expected_exit_code": str(expected_exit_code),
"TEMPLATED_node": _short_path_to_manifest_path(ctx, node.short_path),
"TEMPLATED_repository_args": _short_path_to_manifest_path(ctx, ctx.file._repository_args.short_path),
"TEMPLATED_script_path": script_path,
}
ctx.actions.expand_template(
template = ctx.file._launcher_template,
output = ctx.outputs.script,
substitutions = substitutions,
is_executable = True,
)
node_tool_info = ctx.toolchains["@build_bazel_rules_nodejs//toolchains/node:toolchain_type"].nodeinfo
node_tool_files = []
if node_tool_info.target_tool_path == "" and not node_tool_info.target_tool:
# If tool_path is empty and tool_target is None then there is no local
# node tool, we will just print a nice error message if the user
# attempts to do bazel run
fail("The node toolchain was not properly configured so %s cannot be executed. Make sure that target_tool_path or target_tool is set." % ctx.attr.name)
else:
node_tool = node_tool_info.target_tool_path
if node_tool_info.target_tool:
node_tool_files += node_tool_info.target_tool.files.to_list()
node_tool = _short_path_to_manifest_path(ctx, node_tool_files[0].short_path)

runfiles = depset([node, ctx.outputs.loader, ctx.file._repository_args], transitive = [sources, node_modules])
substitutions = {
"TEMPLATED_args": " ".join([
expand_location_into_runfiles(ctx, a)
for a in ctx.attr.templated_args
]),
"TEMPLATED_env_vars": env_vars,
"TEMPLATED_expected_exit_code": str(expected_exit_code),
"TEMPLATED_node": node_tool,
"TEMPLATED_repository_args": _short_path_to_manifest_path(ctx, ctx.file._repository_args.short_path),
"TEMPLATED_script_path": script_path,
}
ctx.actions.expand_template(
template = ctx.file._launcher_template,
output = ctx.outputs.script,
substitutions = substitutions,
is_executable = True,
)

runfiles = depset(node_tool_files + [ctx.outputs.loader, ctx.file._repository_args], transitive = [sources, node_modules])

# entry point is only needed in runfiles if it is a .js file
if ctx.file.entry_point.extension == "js":
Expand All @@ -192,8 +204,7 @@ def _nodejs_binary_impl(ctx):
executable = ctx.outputs.script,
runfiles = ctx.runfiles(
transitive_files = runfiles,
files = [
node,
files = node_tool_files + [
ctx.outputs.loader,
] + ctx.files._source_map_support_files +

Expand Down Expand Up @@ -292,11 +303,6 @@ _NODEJS_EXECUTABLE_ATTRS = {
in TypeScript.""",
default = True,
),
"node": attr.label(
doc = """The node entry point target.""",
default = Label("@nodejs//:node_bin"),
allow_single_file = True,
),
"node_modules": attr.label(
doc = """The npm packages which should be available to `require()` during
execution.
Expand Down Expand Up @@ -406,6 +412,7 @@ nodejs_binary = rule(
attrs = _NODEJS_EXECUTABLE_ATTRS,
executable = True,
outputs = _NODEJS_EXECUTABLE_OUTPUTS,
toolchains = ["@build_bazel_rules_nodejs//toolchains/node:toolchain_type"],
)
"""Runs some JavaScript code in NodeJS.
"""
Expand All @@ -420,6 +427,7 @@ nodejs_test = rule(
}),
test = True,
outputs = _NODEJS_EXECUTABLE_OUTPUTS,
toolchains = ["@build_bazel_rules_nodejs//toolchains/node:toolchain_type"],
)
"""
Identical to `nodejs_binary`, except this can be used with `bazel test` as well.
Expand Down
40 changes: 20 additions & 20 deletions internal/node/node_labels.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,37 @@
Labels are different on windows and linux/OSX.
"""

def get_node_label(repository_ctx):
if repository_ctx.os.name.lower().find("windows") != -1:
label = Label("@nodejs//:bin/node.cmd")
def get_node_label(os_name):
if os_name.find("windows") != -1:
label = Label("@nodejs_%s//:bin/node.cmd" % os_name)
else:
label = Label("@nodejs//:bin/node")
label = Label("@nodejs_%s//:bin/node" % os_name)
return label

def get_npm_label(repository_ctx):
if repository_ctx.os.name.lower().find("windows") != -1:
label = Label("@nodejs//:bin/npm.cmd")
def get_npm_label(os_name):
if os_name.find("windows") != -1:
label = Label("@nodejs_%s//:bin/npm.cmd" % os_name)
else:
label = Label("@nodejs//:bin/npm")
label = Label("@nodejs_%s//:bin/npm" % os_name)
return label

def get_npm_node_repositories_label(repository_ctx):
if repository_ctx.os.name.lower().find("windows") != -1:
label = Label("@nodejs//:bin/npm_node_repositories.cmd")
def get_npm_node_repositories_label(os_name):
if os_name.find("windows") != -1:
label = Label("@nodejs_%s//:bin/npm_node_repositories.cmd" % os_name)
else:
label = Label("@nodejs//:bin/npm_node_repositories")
label = Label("@nodejs_%s//:bin/npm_node_repositories" % os_name)
return label

def get_yarn_label(repository_ctx):
if repository_ctx.os.name.lower().find("windows") != -1:
label = Label("@nodejs//:bin/yarn.cmd")
def get_yarn_label(os_name):
if os_name.find("windows") != -1:
label = Label("@nodejs_%s//:bin/yarn.cmd" % os_name)
else:
label = Label("@nodejs//:bin/yarn")
label = Label("@nodejs_%s//:bin/yarn" % os_name)
return label

def get_yarn_node_repositories_label(repository_ctx):
if repository_ctx.os.name.lower().find("windows") != -1:
label = Label("@nodejs//:bin/yarn_node_repositories.cmd")
def get_yarn_node_repositories_label(os_name):
if os_name.find("windows") != -1:
label = Label("@nodejs%s//:bin/yarn_node_repositories.cmd" % os_name)
else:
label = Label("@nodejs//:bin/yarn_node_repositories")
label = Label("@nodejs_%s//:bin/yarn_node_repositories" % os_name)
return label
Loading