2017-08-16 12:02:20 -04:00
|
|
|
# Copyright Google Inc. All Rights Reserved.
|
|
|
|
#
|
|
|
|
# Use of this source code is governed by an MIT-style license that can be
|
|
|
|
# found in the LICENSE file at https://angular.io/license
|
2018-01-05 13:53:55 -05:00
|
|
|
"""Implementation of the ng_module rule.
|
|
|
|
"""
|
2017-08-16 12:02:20 -04:00
|
|
|
|
2017-08-21 11:23:47 -04:00
|
|
|
load(":rules_typescript.bzl",
|
|
|
|
"tsc_wrapped_tsconfig",
|
|
|
|
"COMMON_ATTRIBUTES",
|
2017-09-25 15:40:22 -04:00
|
|
|
"COMMON_OUTPUTS",
|
2017-08-21 11:23:47 -04:00
|
|
|
"compile_ts",
|
|
|
|
"DEPS_ASPECTS",
|
|
|
|
"ts_providers_dict_to_struct",
|
2017-08-16 12:02:20 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
# Calculate the expected output of the template compiler for every source in
|
|
|
|
# in the library. Most of these will be produced as empty files but it is
|
|
|
|
# unknown, without parsing, which will be empty.
|
2018-01-05 13:53:55 -05:00
|
|
|
def _expected_outs(ctx):
|
2017-08-21 11:23:47 -04:00
|
|
|
devmode_js_files = []
|
|
|
|
closure_js_files = []
|
|
|
|
declaration_files = []
|
|
|
|
summary_files = []
|
|
|
|
|
|
|
|
for src in ctx.files.srcs + ctx.files.assets:
|
|
|
|
if src.short_path.endswith(".ts") and not src.short_path.endswith(".d.ts"):
|
|
|
|
basename = src.short_path[len(ctx.label.package) + 1:-len(".ts")]
|
|
|
|
devmode_js = [
|
|
|
|
".ngfactory.js",
|
|
|
|
".ngsummary.js",
|
|
|
|
".js",
|
|
|
|
]
|
|
|
|
summaries = [".ngsummary.json"]
|
|
|
|
|
2017-08-16 12:02:20 -04:00
|
|
|
elif src.short_path.endswith(".css"):
|
2017-08-21 11:23:47 -04:00
|
|
|
basename = src.short_path[len(ctx.label.package) + 1:-len(".css")]
|
|
|
|
devmode_js = [
|
|
|
|
".css.shim.ngstyle.js",
|
|
|
|
".css.ngstyle.js",
|
|
|
|
]
|
|
|
|
summaries = []
|
|
|
|
|
2018-01-05 13:53:55 -05:00
|
|
|
else:
|
|
|
|
continue
|
|
|
|
|
2017-08-21 11:23:47 -04:00
|
|
|
closure_js = [f.replace(".js", ".closure.js") for f in devmode_js]
|
|
|
|
declarations = [f.replace(".js", ".d.ts") for f in devmode_js]
|
|
|
|
|
|
|
|
devmode_js_files += [ctx.new_file(ctx.bin_dir, basename + ext) for ext in devmode_js]
|
|
|
|
closure_js_files += [ctx.new_file(ctx.bin_dir, basename + ext) for ext in closure_js]
|
|
|
|
declaration_files += [ctx.new_file(ctx.bin_dir, basename + ext) for ext in declarations]
|
|
|
|
summary_files += [ctx.new_file(ctx.bin_dir, basename + ext) for ext in summaries]
|
|
|
|
|
2017-10-13 19:19:24 -04:00
|
|
|
i18n_messages_files = [ctx.new_file(ctx.genfiles_dir, ctx.label.name + "_ngc_messages.xmb")]
|
2017-09-27 10:38:36 -04:00
|
|
|
|
2017-08-21 11:23:47 -04:00
|
|
|
return struct(
|
|
|
|
closure_js = closure_js_files,
|
|
|
|
devmode_js = devmode_js_files,
|
|
|
|
declarations = declaration_files,
|
|
|
|
summaries = summary_files,
|
2017-09-27 10:38:36 -04:00
|
|
|
i18n_messages = i18n_messages_files,
|
2017-08-21 11:23:47 -04:00
|
|
|
)
|
2017-08-16 12:02:20 -04:00
|
|
|
|
|
|
|
def _ngc_tsconfig(ctx, files, srcs, **kwargs):
|
2018-01-05 13:53:55 -05:00
|
|
|
outs = _expected_outs(ctx)
|
2017-08-21 11:23:47 -04:00
|
|
|
if "devmode_manifest" in kwargs:
|
|
|
|
expected_outs = outs.devmode_js + outs.declarations + outs.summaries
|
|
|
|
else:
|
|
|
|
expected_outs = outs.closure_js
|
|
|
|
|
2017-08-16 12:02:20 -04:00
|
|
|
return dict(tsc_wrapped_tsconfig(ctx, files, srcs, **kwargs), **{
|
|
|
|
"angularCompilerOptions": {
|
2017-08-21 11:23:47 -04:00
|
|
|
"generateCodeForLibraries": False,
|
2017-09-21 21:05:07 -04:00
|
|
|
"allowEmptyCodegenFiles": True,
|
2017-10-02 20:17:14 -04:00
|
|
|
"enableSummariesForJit": True,
|
2018-01-10 19:18:26 -05:00
|
|
|
"fullTemplateTypeCheck": ctx.attr.type_check,
|
2017-08-21 11:23:47 -04:00
|
|
|
# FIXME: wrong place to de-dupe
|
2017-12-04 14:12:05 -05:00
|
|
|
"expectedOut": depset([o.path for o in expected_outs]).to_list(),
|
|
|
|
"preserveWhitespaces": False,
|
2017-08-16 12:02:20 -04:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2017-08-21 11:23:47 -04:00
|
|
|
def _collect_summaries_aspect_impl(target, ctx):
|
2018-01-05 13:53:55 -05:00
|
|
|
results = depset(target.angular.summaries if hasattr(target, "angular") else [])
|
2017-08-21 11:23:47 -04:00
|
|
|
|
|
|
|
# If we are visiting empty-srcs ts_library, this is a re-export
|
2017-08-30 19:10:58 -04:00
|
|
|
srcs = ctx.rule.attr.srcs if hasattr(ctx.rule.attr, "srcs") else []
|
2017-08-21 11:23:47 -04:00
|
|
|
|
|
|
|
# "re-export" rules should expose all the files of their deps
|
|
|
|
if not srcs:
|
|
|
|
for dep in ctx.rule.attr.deps:
|
|
|
|
if (hasattr(dep, "angular")):
|
2018-01-05 13:53:55 -05:00
|
|
|
results = depset(dep.angular.summaries, transitive = [results])
|
2017-08-21 11:23:47 -04:00
|
|
|
|
|
|
|
return struct(collect_summaries_aspect_result = results)
|
|
|
|
|
|
|
|
_collect_summaries_aspect = aspect(
|
|
|
|
implementation = _collect_summaries_aspect_impl,
|
|
|
|
attr_aspects = ["deps"],
|
|
|
|
)
|
|
|
|
|
2017-09-27 10:38:36 -04:00
|
|
|
# Extra options passed to Node when running ngc.
|
|
|
|
_EXTRA_NODE_OPTIONS_FLAGS = [
|
|
|
|
# Expose the v8 garbage collection API to JS.
|
|
|
|
"--node_options=--expose-gc"
|
|
|
|
]
|
|
|
|
|
|
|
|
def ngc_compile_action(ctx, label, inputs, outputs, messages_out, config_file_path,
|
2018-01-05 13:53:55 -05:00
|
|
|
locale=None, i18n_args=[]):
|
|
|
|
"""Helper function to create the ngc action.
|
|
|
|
|
|
|
|
This is exposed for google3 to wire up i18n replay rules, and is not intended
|
|
|
|
as part of the public API.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
ctx: skylark context
|
|
|
|
label: the label of the ng_module being compiled
|
|
|
|
inputs: passed to the ngc action's inputs
|
|
|
|
outputs: passed to the ngc action's outputs
|
|
|
|
messages_out: produced xmb files
|
|
|
|
config_file_path: path to the tsconfig file
|
|
|
|
locale: i18n locale, or None
|
|
|
|
i18n_args: additional command-line arguments to ngc
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
the parameters of the compilation which will be used to replay the ngc action for i18N.
|
|
|
|
"""
|
|
|
|
|
2017-09-27 10:38:36 -04:00
|
|
|
mnemonic = "AngularTemplateCompile"
|
|
|
|
progress_message = "Compiling Angular templates (ngc) %s" % label
|
2018-01-05 13:53:55 -05:00
|
|
|
|
2017-09-27 10:38:36 -04:00
|
|
|
if locale:
|
|
|
|
mnemonic = "AngularI18NMerging"
|
|
|
|
supports_workers = "0"
|
|
|
|
progress_message = ("Recompiling Angular templates (ngc) %s for locale %s" %
|
|
|
|
(label, locale))
|
|
|
|
else:
|
|
|
|
supports_workers = str(int(ctx.attr._supports_workers))
|
|
|
|
|
2017-10-13 19:26:21 -04:00
|
|
|
arguments = list(_EXTRA_NODE_OPTIONS_FLAGS)
|
2017-09-27 10:38:36 -04:00
|
|
|
# One at-sign makes this a params-file, enabling the worker strategy.
|
|
|
|
# Two at-signs escapes the argument so it's passed through to ngc
|
|
|
|
# rather than the contents getting expanded.
|
|
|
|
if supports_workers == "1":
|
|
|
|
arguments += ["@@" + config_file_path]
|
|
|
|
else:
|
|
|
|
arguments += ["-p", config_file_path]
|
|
|
|
|
|
|
|
arguments += i18n_args
|
|
|
|
|
|
|
|
ctx.action(
|
|
|
|
progress_message = progress_message,
|
|
|
|
mnemonic = mnemonic,
|
|
|
|
inputs = inputs,
|
|
|
|
outputs = outputs,
|
|
|
|
arguments = arguments,
|
|
|
|
executable = ctx.executable.compiler,
|
|
|
|
execution_requirements = {
|
|
|
|
"supports-workers": supports_workers,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
if messages_out != None:
|
|
|
|
ctx.action(inputs = list(inputs),
|
|
|
|
outputs = messages_out,
|
|
|
|
executable = ctx.executable._ng_xi18n,
|
|
|
|
arguments = (_EXTRA_NODE_OPTIONS_FLAGS +
|
|
|
|
[config_file_path] +
|
2017-10-13 19:19:24 -04:00
|
|
|
# The base path is bin_dir because of the way the ngc
|
|
|
|
# compiler host is configured. So we need to explictily
|
|
|
|
# point to genfiles/ to redirect the output.
|
|
|
|
["../genfiles/" + messages_out[0].short_path]),
|
2017-09-27 10:38:36 -04:00
|
|
|
progress_message = "Extracting Angular 2 messages (ng_xi18n)",
|
|
|
|
mnemonic = "Angular2MessageExtractor")
|
|
|
|
|
|
|
|
if not locale and not ctx.attr.no_i18n:
|
|
|
|
return struct(
|
|
|
|
label = label,
|
|
|
|
tsconfig = config_file_path,
|
|
|
|
inputs = inputs,
|
|
|
|
outputs = outputs,
|
|
|
|
)
|
|
|
|
|
2018-01-05 13:53:55 -05:00
|
|
|
return None
|
2017-08-21 11:23:47 -04:00
|
|
|
|
2018-01-05 13:53:55 -05:00
|
|
|
def _compile_action(ctx, inputs, outputs, messages_out, config_file_path):
|
|
|
|
# Give the Angular compiler all the user-listed assets
|
|
|
|
file_inputs = list(ctx.files.assets)
|
2017-08-21 11:23:47 -04:00
|
|
|
|
2018-01-05 13:53:55 -05:00
|
|
|
# The compiler only needs to see TypeScript sources from the npm dependencies,
|
|
|
|
# but may need to look at package.json and ngsummary.json files as well.
|
2017-08-16 12:02:20 -04:00
|
|
|
if hasattr(ctx.attr, "node_modules"):
|
2018-01-05 13:53:55 -05:00
|
|
|
file_inputs += [f for f in ctx.files.node_modules
|
|
|
|
if f.path.endswith(".ts") or f.path.endswith(".json")]
|
|
|
|
|
|
|
|
# If the user supplies a tsconfig.json file, the Angular compiler needs to read it
|
2017-08-21 11:23:47 -04:00
|
|
|
if hasattr(ctx.attr, "tsconfig") and ctx.file.tsconfig:
|
2018-01-05 13:53:55 -05:00
|
|
|
file_inputs.append(ctx.file.tsconfig)
|
|
|
|
|
|
|
|
# Collect the inputs and summary files from our deps
|
|
|
|
action_inputs = depset(file_inputs,
|
|
|
|
transitive = [inputs] + [dep.collect_summaries_aspect_result for dep in ctx.attr.deps
|
|
|
|
if hasattr(dep, "collect_summaries_aspect_result")])
|
2017-08-16 12:02:20 -04:00
|
|
|
|
2017-09-27 10:38:36 -04:00
|
|
|
return ngc_compile_action(ctx, ctx.label, action_inputs, outputs, messages_out, config_file_path)
|
2017-08-16 12:02:20 -04:00
|
|
|
|
|
|
|
|
2017-08-21 11:23:47 -04:00
|
|
|
def _prodmode_compile_action(ctx, inputs, outputs, config_file_path):
|
2018-01-05 13:53:55 -05:00
|
|
|
outs = _expected_outs(ctx)
|
2017-09-27 10:38:36 -04:00
|
|
|
return _compile_action(ctx, inputs, outputs + outs.closure_js, outs.i18n_messages, config_file_path)
|
2017-08-16 12:02:20 -04:00
|
|
|
|
2017-08-21 11:23:47 -04:00
|
|
|
def _devmode_compile_action(ctx, inputs, outputs, config_file_path):
|
2018-01-05 13:53:55 -05:00
|
|
|
outs = _expected_outs(ctx)
|
2017-09-27 10:38:36 -04:00
|
|
|
_compile_action(ctx, inputs, outputs + outs.devmode_js + outs.declarations + outs.summaries, None, config_file_path)
|
2017-08-21 11:23:47 -04:00
|
|
|
|
2018-01-05 13:53:55 -05:00
|
|
|
def _ts_expected_outs(ctx, label):
|
|
|
|
# rules_typescript expects a function with two arguments, but our
|
|
|
|
# implementation doesn't use the label
|
|
|
|
_ignored = [label]
|
|
|
|
return _expected_outs(ctx)
|
|
|
|
|
2017-08-21 11:23:47 -04:00
|
|
|
def ng_module_impl(ctx, ts_compile_actions):
|
2018-01-05 13:53:55 -05:00
|
|
|
"""Implementation function for the ng_module rule.
|
|
|
|
|
|
|
|
This is exposed so that google3 can have its own entry point that re-uses this
|
|
|
|
and is not meant as a public API.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
ctx: the skylark rule context
|
|
|
|
ts_compile_actions: generates all the actions to run an ngc compilation
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
the result of the ng_module rule as a dict, suitable for
|
|
|
|
conversion by ts_providers_dict_to_struct
|
|
|
|
"""
|
|
|
|
|
2017-08-21 11:23:47 -04:00
|
|
|
providers = ts_compile_actions(
|
|
|
|
ctx, is_library=True, compile_action=_prodmode_compile_action,
|
|
|
|
devmode_compile_action=_devmode_compile_action,
|
|
|
|
tsc_wrapped_tsconfig=_ngc_tsconfig,
|
2018-01-05 13:53:55 -05:00
|
|
|
outputs = _ts_expected_outs)
|
2017-08-21 11:23:47 -04:00
|
|
|
|
2018-01-05 13:53:55 -05:00
|
|
|
outs = _expected_outs(ctx)
|
2017-08-21 11:23:47 -04:00
|
|
|
providers["angular"] = {
|
2018-01-05 13:53:55 -05:00
|
|
|
"summaries": _expected_outs(ctx).summaries
|
2017-08-16 12:02:20 -04:00
|
|
|
}
|
2017-09-27 10:38:36 -04:00
|
|
|
providers["ngc_messages"] = outs.i18n_messages
|
2017-08-16 12:02:20 -04:00
|
|
|
|
2017-08-21 11:23:47 -04:00
|
|
|
return providers
|
|
|
|
|
2017-08-16 12:02:20 -04:00
|
|
|
def _ng_module_impl(ctx):
|
2017-08-21 11:23:47 -04:00
|
|
|
return ts_providers_dict_to_struct(ng_module_impl(ctx, compile_ts))
|
2017-08-16 12:02:20 -04:00
|
|
|
|
2017-08-21 11:23:47 -04:00
|
|
|
NG_MODULE_ATTRIBUTES = {
|
|
|
|
"srcs": attr.label_list(allow_files = [".ts"]),
|
2017-08-16 12:02:20 -04:00
|
|
|
|
2017-08-21 11:23:47 -04:00
|
|
|
"deps": attr.label_list(aspects = DEPS_ASPECTS + [_collect_summaries_aspect]),
|
2017-08-16 12:02:20 -04:00
|
|
|
|
2017-08-21 11:23:47 -04:00
|
|
|
"assets": attr.label_list(allow_files = [
|
|
|
|
".css",
|
|
|
|
# TODO(alexeagle): change this to ".ng.html" when usages updated
|
|
|
|
".html",
|
|
|
|
]),
|
|
|
|
|
2018-01-10 19:18:26 -05:00
|
|
|
"type_check": attr.bool(default = True),
|
|
|
|
|
2017-08-21 11:23:47 -04:00
|
|
|
"no_i18n": attr.bool(default = False),
|
|
|
|
|
|
|
|
"compiler": attr.label(
|
|
|
|
default = Label("//src/ngc-wrapped"),
|
|
|
|
executable = True,
|
|
|
|
cfg = "host",
|
|
|
|
),
|
|
|
|
|
2017-09-27 10:38:36 -04:00
|
|
|
"_ng_xi18n": attr.label(
|
|
|
|
default = Label("//src/ngc-wrapped:xi18n"),
|
|
|
|
executable = True,
|
|
|
|
cfg = "host",
|
|
|
|
),
|
|
|
|
|
2017-08-30 01:09:55 -04:00
|
|
|
"_supports_workers": attr.bool(default = True),
|
2017-08-21 11:23:47 -04:00
|
|
|
}
|
2017-08-16 12:02:20 -04:00
|
|
|
|
|
|
|
ng_module = rule(
|
|
|
|
implementation = _ng_module_impl,
|
2018-01-05 13:53:55 -05:00
|
|
|
attrs = dict(dict(COMMON_ATTRIBUTES, **NG_MODULE_ATTRIBUTES), **{
|
2017-08-16 12:02:20 -04:00
|
|
|
"tsconfig": attr.label(allow_files = True, single_file = True),
|
2017-08-21 11:23:47 -04:00
|
|
|
|
2017-08-16 12:02:20 -04:00
|
|
|
# @// is special syntax for the "main" repository
|
|
|
|
# The default assumes the user specified a target "node_modules" in their
|
|
|
|
# root BUILD file.
|
|
|
|
"node_modules": attr.label(
|
|
|
|
default = Label("@//:node_modules")
|
|
|
|
),
|
2018-01-05 13:53:55 -05:00
|
|
|
}),
|
2017-09-25 15:40:22 -04:00
|
|
|
outputs = COMMON_OUTPUTS,
|
2017-10-13 19:26:21 -04:00
|
|
|
)
|