/
node_build.py
88 lines (73 loc) · 3.84 KB
/
node_build.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# coding=utf-8
# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).
from __future__ import (absolute_import, division, generators, nested_scopes, print_function,
unicode_literals, with_statement)
import os
from collections import defaultdict
from pants.backend.jvm.tasks.classpath_products import ClasspathProducts
from pants.base.exceptions import TaskError
from pants.base.workunit import WorkUnitLabel
from pants.goal.products import MultipleRootedProducts
from pants.util.contextutil import pushd
from pants.util.dirutil import absolute_symlink
from pants.contrib.node.tasks.node_paths import NodePaths
from pants.contrib.node.tasks.node_task import NodeTask
class NodeBuild(NodeTask):
"""Create an archive bundle of NodeModule targets."""
@classmethod
def product_types(cls):
# runtime_classpath is used for JVM target to include node build results as resources.
return ['bundleable_js', 'runtime_classpath']
@classmethod
def prepare(cls, options, round_manager):
super(NodeBuild, cls).prepare(options, round_manager)
round_manager.require_data(NodePaths)
@property
def create_target_dirs(self):
return True
def _run_build_script(self, target, results_dir, node_installed_path):
target_address = target.address.reference()
# If there is build script defined, run the build script and return build output directory;
# If there is not build script defined, use installation directory as build output.
if target.payload.build_script:
self.context.log.info('Running node build {} for {} at {}\n'.format(
target.payload.build_script, target_address, node_installed_path))
result, npm_build_command = self.execute_npm(
['run-script', target.payload.build_script],
workunit_name=target_address,
workunit_labels=[WorkUnitLabel.COMPILER])
# Make sure script run is successful.
if result != 0:
raise TaskError(
'Failed to run build for {}:\n\t{} failed with exit code {}'.format(
target_address, npm_build_command, result))
def _get_output_dir(self, target, node_installed_path):
return os.path.normpath(os.path.join(
node_installed_path,
target.payload.output_dir if target.payload.build_script else ''))
def execute(self):
node_paths = self.context.products.get_data(NodePaths)
runtime_classpath_product = self.context.products.get_data(
'runtime_classpath', init_func=ClasspathProducts.init_func(self.get_options().pants_workdir))
bundleable_js_product = self.context.products.get_data(
'bundleable_js', init_func=lambda: defaultdict(MultipleRootedProducts))
targets = self.context.targets(predicate=self.is_node_module)
with self.invalidated(targets, invalidate_dependents=True) as invalidation_check:
for vt in invalidation_check.all_vts:
target = vt.target
node_installed_path = node_paths.node_path(target)
with pushd(node_installed_path):
if not vt.valid:
self._run_build_script(target, vt.results_dir, node_installed_path)
if not target.payload.dev_dependency:
output_dir = self._get_output_dir(target, node_installed_path)
# Make sure that there is output generated.
if not os.path.exists(output_dir):
raise TaskError(
'Target {} has build script {} specified, but did not generate any output '
'at {}.\n'.format(
target.address.reference(), target.payload.build_script, output_dir))
absolute_symlink(output_dir, os.path.join(vt.results_dir, target.address.target_name))
bundleable_js_product[target].add_abs_paths(output_dir, [output_dir])
runtime_classpath_product.add_for_target(target, [('default', vt.results_dir)])