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

docs(bazel): add skydoc generation #23544

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Expand Up @@ -48,7 +48,7 @@ jobs:

# Check BUILD.bazel formatting before we have a node_modules directory
# Then we don't need any exclude pattern to avoid checking those files
- run: 'buildifier -mode=check $(find . -type f \( -name BUILD.bazel -or -name BUILD \)) ||
- run: 'buildifier -mode=check $(find . -type f \( -name "*.bzl" -or -name BUILD.bazel -or -name BUILD \)) ||
(echo "BUILD files not formatted. Please run ''yarn buildifier''" ; exit 1)'
# Run the skylark linter to check our Bazel rules
# deprecated-api is disabled because we use actions.new_file(genfiles_dir)
Expand Down
24 changes: 24 additions & 0 deletions WORKSPACE
Expand Up @@ -38,6 +38,13 @@ http_archive(
sha256 = "feba3278c13cde8d67e341a837f69a029f698d7a27ddbb2a202be7a10b22142a",
)

http_archive(
name = "io_bazel_rules_sass",
url = "https://github.com/bazelbuild/rules_sass/archive/0.1.0.zip",
strip_prefix = "rules_sass-0.1.0",
sha256 = "b243c4d64f054c174051785862ab079050d90b37a1cef7da93821c6981cb9ad4",
)

# This commit matches the version of buildifier in angular/ngcontainer
# If you change this, also check if it matches the version in the angular/ngcontainer
# version in /.circleci/config.yml
Expand All @@ -58,6 +65,14 @@ http_archive(
sha256 = "e373d2ae24955c1254c495c9c421c009d88966565c35e4e8444c082cb1f0f48f",
)

http_archive(
name = "io_bazel_skydoc",
# TODO: switch to upstream when https://github.com/bazelbuild/skydoc/pull/103 is merged
url = "https://github.com/alexeagle/skydoc/archive/fe2e9f888d28e567fef62ec9d4a93c425526d701.zip",
strip_prefix = "skydoc-fe2e9f888d28e567fef62ec9d4a93c425526d701",
sha256 = "7bfb5545f59792a2745f2523b9eef363f9c3e7274791c030885e7069f8116016",
)

# We have a source dependency on the Devkit repository, because it's built with
# Bazel.
# This allows us to edit sources and have the effect appear immediately without
Expand Down Expand Up @@ -144,3 +159,12 @@ yarn_install(
package_json = "//tools/http-server:package.json",
yarn_lock = "//tools/http-server:yarn.lock",
)

##################################
# Skylark documentation generation

load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")
sass_repositories()

load("@io_bazel_skydoc//skylark:skylark.bzl", "skydoc_repositories")
skydoc_repositories()
6 changes: 4 additions & 2 deletions index.bzl
Expand Up @@ -12,11 +12,13 @@ from source downstream. Alternately, this API is available from the
used in a downstream project.
"""

load("//packages/bazel:index.bzl",
load(
"//packages/bazel:index.bzl",
_ng_module = "ng_module",
_ng_package = "ng_package",
_protractor_web_test = "protractor_web_test",
_protractor_web_test_suite = "protractor_web_test_suite")
_protractor_web_test_suite = "protractor_web_test_suite",
)
load("//tools:ng_setup_workspace.bzl", _ng_setup_workspace = "ng_setup_workspace")

ng_module = _ng_module
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -19,7 +19,7 @@
"preskylint": "bazel build --noshow_progress @io_bazel//src/tools/skylark/java/com/google/devtools/skylark/skylint:Skylint",
"skylint": "find . -type f -name \"*.bzl\" ! -path \"*/node_modules/*\" ! -path \"./dist/*\" | xargs $(bazel info bazel-bin)/external/io_bazel/src/tools/skylark/java/com/google/devtools/skylark/skylint/Skylint --disable-checks=deprecated-api",
"prebuildifier": "bazel build --noshow_progress @com_github_bazelbuild_buildtools//buildifier",
"buildifier": "find . -type f \\( -name BUILD -or -name BUILD.bazel \\) ! -path \"*/node_modules/*\" | xargs $(bazel info bazel-bin)/external/com_github_bazelbuild_buildtools/buildifier/*/buildifier",
"buildifier": "find . -type f \\( -name \"*.bzl\" -or -name BUILD -or -name BUILD.bazel \\) ! -path \"*/node_modules/*\" | xargs $(bazel info bazel-bin)/external/com_github_bazelbuild_buildtools/buildifier/*/buildifier",
"preinstall": "node tools/yarn/check-yarn.js",
"postinstall": "yarn update-webdriver && node ./tools/postinstall-patches.js && yarn patch-types",
"//patch-types": "work-around for issue https://github.com/angular/angular/issues/25051",
Expand Down
1 change: 1 addition & 0 deletions packages/bazel/BUILD.bazel
Expand Up @@ -14,6 +14,7 @@ npm_package(
"package.json",
"//packages/bazel/src:package_assets",
],
packages = ["//packages/bazel/docs"],
# Re-host //packages/bazel/ which is just // in the public distro
replacements = {
"//packages/bazel/": "//",
Expand Down
16 changes: 16 additions & 0 deletions packages/bazel/docs/BUILD.bazel
@@ -0,0 +1,16 @@
load("@io_bazel_skydoc//skylark:skylark.bzl", "skylark_doc")

skylark_doc(
name = "docs",
srcs = [
"//packages/bazel/src:ng_module.bzl",
"//packages/bazel/src:ng_rollup_bundle.bzl",
"//packages/bazel/src:ng_setup_workspace.bzl",
"//packages/bazel/src/ng_package:ng_package.bzl",
"//packages/bazel/src/protractor:protractor_web_test.bzl",
],
format = "html",
site_root = "/bazel-builds",
strip_prefix = "packages/bazel/",
visibility = ["//packages/bazel:__subpackages__"],
)
10 changes: 7 additions & 3 deletions packages/bazel/index.bzl
Expand Up @@ -10,12 +10,16 @@ Users should not load files under "/src"
load("//packages/bazel/src:ng_module.bzl", _ng_module = "ng_module")
load("//packages/bazel/src:ng_setup_workspace.bzl", _ng_setup_workspace = "ng_setup_workspace")
load("//packages/bazel/src/ng_package:ng_package.bzl", _ng_package = "ng_package")
load("//packages/bazel/src/protractor:protractor_web_test.bzl",
_protractor_web_test = "protractor_web_test",
_protractor_web_test_suite = "protractor_web_test_suite")
load(
"//packages/bazel/src/protractor:protractor_web_test.bzl",
_protractor_web_test = "protractor_web_test",
_protractor_web_test_suite = "protractor_web_test_suite",
)

ng_module = _ng_module
ng_package = _ng_package
protractor_web_test = _protractor_web_test
protractor_web_test_suite = _protractor_web_test_suite
ng_setup_workspace = _ng_setup_workspace
# DO NOT ADD PUBLIC API without including in the documentation generation
# Run `bazel build //packages/bazel/docs` to verify
215 changes: 110 additions & 105 deletions packages/bazel/src/esm5.bzl
Expand Up @@ -28,88 +28,92 @@ ESM5Info = provider(
)

def _map_closure_path(file):
result = file.short_path[:-len(".closure.js")]
# short_path is meant to be used when accessing runfiles in a binary, where
# the CWD is inside the current repo. Therefore files in external repo have a
# short_path of ../external/wkspc/path/to/package
# We want to strip the first two segments from such paths.
if (result.startswith("../")):
result = "/".join(result.split("/")[2:])
return result + ".js"
result = file.short_path[:-len(".closure.js")]

# short_path is meant to be used when accessing runfiles in a binary, where
# the CWD is inside the current repo. Therefore files in external repo have a
# short_path of ../external/wkspc/path/to/package
# We want to strip the first two segments from such paths.
if (result.startswith("../")):
result = "/".join(result.split("/")[2:])
return result + ".js"

def _join(array):
return "/".join([p for p in array if p])
return "/".join([p for p in array if p])

def _esm5_outputs_aspect(target, ctx):
if not hasattr(target, "typescript"):
return []

# We create a new tsconfig.json file that will have our compilation settings
tsconfig = ctx.actions.declare_file("%s_esm5.tsconfig.json" % target.label.name)

workspace = target.label.workspace_root if target.label.workspace_root else ""

# re-root the outputs under a ".esm5" directory so the path don't collide
out_dir = ctx.label.name + ".esm5"
if workspace:
out_dir = out_dir + "/" + workspace

outputs = [ctx.actions.declare_file(_join([out_dir, _map_closure_path(f)]))
for f in target.typescript.replay_params.outputs
if not f.short_path.endswith(".externs.js")]

ctx.actions.run(
executable = ctx.executable._modify_tsconfig,
inputs = [target.typescript.replay_params.tsconfig],
outputs = [tsconfig],
arguments = [
target.typescript.replay_params.tsconfig.path,
tsconfig.path,
_join([workspace, target.label.package, ctx.label.name + ".esm5"]),
ctx.bin_dir.path
],
)

ctx.actions.run(
progress_message = "Compiling TypeScript (ES5 with ES Modules) %s" % target.label,
inputs = target.typescript.replay_params.inputs + [tsconfig],
outputs = outputs,
arguments = [tsconfig.path],
executable = target.typescript.replay_params.compiler,
execution_requirements = {
# TODO(alexeagle): enable worker mode for these compilations
"supports-workers": "0",
},
)

root_dir = _join([
ctx.bin_dir.path,
workspace,
target.label.package,
ctx.label.name + ".esm5",
])

transitive_output={root_dir: depset(outputs)}
for dep in ctx.rule.attr.deps:
if ESM5Info in dep:
transitive_output.update(dep[ESM5Info].transitive_output)

return [ESM5Info(
transitive_output = transitive_output,
)]
if not hasattr(target, "typescript"):
return []

# We create a new tsconfig.json file that will have our compilation settings
tsconfig = ctx.actions.declare_file("%s_esm5.tsconfig.json" % target.label.name)

workspace = target.label.workspace_root if target.label.workspace_root else ""

# re-root the outputs under a ".esm5" directory so the path don't collide
out_dir = ctx.label.name + ".esm5"
if workspace:
out_dir = out_dir + "/" + workspace

outputs = [
ctx.actions.declare_file(_join([out_dir, _map_closure_path(f)]))
for f in target.typescript.replay_params.outputs
if not f.short_path.endswith(".externs.js")
]

ctx.actions.run(
executable = ctx.executable._modify_tsconfig,
inputs = [target.typescript.replay_params.tsconfig],
outputs = [tsconfig],
arguments = [
target.typescript.replay_params.tsconfig.path,
tsconfig.path,
_join([workspace, target.label.package, ctx.label.name + ".esm5"]),
ctx.bin_dir.path,
],
)

ctx.actions.run(
progress_message = "Compiling TypeScript (ES5 with ES Modules) %s" % target.label,
inputs = target.typescript.replay_params.inputs + [tsconfig],
outputs = outputs,
arguments = [tsconfig.path],
executable = target.typescript.replay_params.compiler,
execution_requirements = {
# TODO(alexeagle): enable worker mode for these compilations
"supports-workers": "0",
},
)

root_dir = _join([
ctx.bin_dir.path,
workspace,
target.label.package,
ctx.label.name + ".esm5",
])

transitive_output = {root_dir: depset(outputs)}
for dep in ctx.rule.attr.deps:
if ESM5Info in dep:
transitive_output.update(dep[ESM5Info].transitive_output)

return [ESM5Info(
transitive_output = transitive_output,
)]

# Downstream rules can use this aspect to access the ESM5 output flavor.
# Only terminal rules (those which expect never to be used in deps[]) should do
# this.
esm5_outputs_aspect = aspect(
implementation = _esm5_outputs_aspect,
# Recurse to the deps of any target we visit
attr_aspects = ['deps'],
attr_aspects = ["deps"],
attrs = {
"_modify_tsconfig": attr.label(
default = Label("//packages/bazel/src:modify_tsconfig"),
executable = True,
cfg = "host"),
cfg = "host",
),
# We must list tsc_wrapped here to ensure it's built before the action runs
# For some reason, having the compiler output as an input to the action above
# is not sufficient.
Expand All @@ -128,43 +132,44 @@ esm5_outputs_aspect = aspect(
)

def esm5_root_dir(ctx):
return ctx.label.name + ".esm5"
return ctx.label.name + ".esm5"

def flatten_esm5(ctx):
"""Merge together the .esm5 folders from the dependencies.

Two different dependencies A and B may have outputs like
`bazel-bin/path/to/A.esm5/path/to/lib.js`
`bazel-bin/path/to/B.esm5/path/to/main.js`

In order to run rollup on this app, in case main.js contains `import from './lib'`
they need to be together in the same root directory, so if we depend on both A and B
we need the outputs to be
`bazel-bin/path/to/my_rule.esm5/path/to/lib.js`
`bazel-bin/path/to/my_rule.esm5/path/to/main.js`

Args:
ctx: the skylark rule execution context

Returns:
list of flattened files
"""
esm5_sources = []
result = []
for dep in ctx.attr.deps:
if ESM5Info in dep:
transitive_output = dep[ESM5Info].transitive_output
esm5_sources.extend(transitive_output.values())
for f in depset(transitive = esm5_sources).to_list():
path = f.short_path[f.short_path.find(".esm5") + len(".esm5"):]
if (path.startswith("../")):
path = "external/" + path[3:]
rerooted_file = ctx.actions.declare_file("/".join([esm5_root_dir(ctx), path]))
result.append(rerooted_file)
# print("copy", f.short_path, "to", rerooted_file.short_path)
ctx.actions.expand_template(
output = rerooted_file,
template = f,
substitutions = {},
)
return result
"""Merge together the .esm5 folders from the dependencies.

Two different dependencies A and B may have outputs like
`bazel-bin/path/to/A.esm5/path/to/lib.js`
`bazel-bin/path/to/B.esm5/path/to/main.js`

In order to run rollup on this app, in case main.js contains `import from './lib'`
they need to be together in the same root directory, so if we depend on both A and B
we need the outputs to be
`bazel-bin/path/to/my_rule.esm5/path/to/lib.js`
`bazel-bin/path/to/my_rule.esm5/path/to/main.js`

Args:
ctx: the skylark rule execution context

Returns:
list of flattened files
"""
esm5_sources = []
result = []
for dep in ctx.attr.deps:
if ESM5Info in dep:
transitive_output = dep[ESM5Info].transitive_output
esm5_sources.extend(transitive_output.values())
for f in depset(transitive = esm5_sources).to_list():
path = f.short_path[f.short_path.find(".esm5") + len(".esm5"):]
if (path.startswith("../")):
path = "external/" + path[3:]
rerooted_file = ctx.actions.declare_file("/".join([esm5_root_dir(ctx), path]))
result.append(rerooted_file)

# print("copy", f.short_path, "to", rerooted_file.short_path)
ctx.actions.expand_template(
output = rerooted_file,
template = f,
substitutions = {},
)
return result