Makefile 40.4 KB
Newer Older
1 2 3
-include config.mk

BUILDTYPE ?= Release
4
PYTHON ?= python
5 6
DESTDIR ?=
SIGN ?=
7 8 9 10
PREFIX ?= /usr/local
FLAKY_TESTS ?= run
TEST_CI_ARGS ?=
STAGINGSERVER ?= node-www
11
LOGLEVEL ?= silent
12
OSTYPE := $(shell uname -s | tr '[A-Z]' '[a-z]')
13
COVTESTS ?= test-cov
14 15
GTEST_FILTER ?= "*"
GNUMAKEFLAGS += --no-print-directory
16
GCOV ?= gcov
17
PWD = $(CURDIR)
18

19 20
ifdef JOBS
  PARALLEL_ARGS = -j $(JOBS)
21 22
else
  PARALLEL_ARGS = -J
23 24 25
endif

ifdef ENABLE_V8_TAP
26 27 28
  TAP_V8 := --junitout $(PWD)/v8-tap.xml
  TAP_V8_INTL := --junitout $(PWD)/v8-intl-tap.xml
  TAP_V8_BENCHMARKS := --junitout $(PWD)/v8-benchmarks-tap.xml
29 30
endif

31
V8_TEST_OPTIONS = $(V8_EXTRA_TEST_OPTIONS)
32 33 34 35
ifdef DISABLE_V8_I18N
  V8_BUILD_OPTIONS += i18nsupport=off
endif

36 37 38 39
ifeq ($(OSTYPE), darwin)
  GCOV = xcrun llvm-cov gcov
endif

40 41
BUILDTYPE_LOWER := $(shell echo $(BUILDTYPE) | tr '[A-Z]' '[a-z]')

42 43 44 45 46
# Determine EXEEXT
EXEEXT := $(shell $(PYTHON) -c \
		"import sys; print('.exe' if sys.platform == 'win32' else '')")

NODE_EXE = node$(EXEEXT)
47
NODE ?= ./$(NODE_EXE)
48
NODE_G_EXE = node_g$(EXEEXT)
49
NPM ?= ./deps/npm/bin/npm-cli.js
50 51 52 53 54

# Flags for packaging.
BUILD_DOWNLOAD_FLAGS ?= --download=all
BUILD_INTL_FLAGS ?= --with-intl=small-icu
BUILD_RELEASE_FLAGS ?= $(BUILD_DOWNLOAD_FLAGS) $(BUILD_INTL_FLAGS)
55

56 57 58 59
# Default to verbose builds.
# To do quiet/pretty builds, run `make V=` to set V to an empty string,
# or set the V environment variable to an empty string.
V ?= 1
60

61
.PHONY: all
62 63 64
# BUILDTYPE=Debug builds both release and debug builds. If you want to compile
# just the debug build, run `make -C out BUILDTYPE=Debug` instead.
ifeq ($(BUILDTYPE),Release)
65
all: out/Makefile $(NODE_EXE) ## Default target, builds node in out/Release/node.
66
else
67
all: out/Makefile $(NODE_EXE) $(NODE_G_EXE)
68
endif
69

70
.PHONY: help
71 72 73 74 75 76
# To add a target to the help, add a double comment (##) on the target line.
help: ## Print help for targets with comments.
	@printf "For more targets and info see the comments in the Makefile.\n\n"
	@grep -E '^[a-zA-Z0-9._-]+:.*?## .*$$' Makefile | sort | \
		awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}'

77 78
# The .PHONY is needed to ensure that we recursively use the out/Makefile
# to check for changes.
79
.PHONY: $(NODE_EXE) $(NODE_G_EXE)
80

81 82 83 84 85
# The -r/-L check stops it recreating the link if it is already in place,
# otherwise $(NODE_EXE) being a .PHONY target means it is always re-run.
# Without the check there is a race condition between the link being deleted
# and recreated which can break the addons build when running test-ci
# See comments on the build-addons target for some more info
86
$(NODE_EXE): config.gypi out/Makefile
87
	$(MAKE) -C out BUILDTYPE=Release V=$(V)
88
	if [ ! -r $@ -o ! -L $@ ]; then ln -fs out/Release/$(NODE_EXE) $@; fi
89

90
$(NODE_G_EXE): config.gypi out/Makefile
91
	$(MAKE) -C out BUILDTYPE=Debug V=$(V)
92
	if [ ! -r $@ -o ! -L $@ ]; then ln -fs out/Debug/$(NODE_EXE) $@; fi
93

94 95
out/Makefile: common.gypi deps/uv/uv.gyp deps/http_parser/http_parser.gyp \
              deps/zlib/zlib.gyp deps/v8/gypfiles/toolchain.gypi \
96
              deps/v8/gypfiles/features.gypi deps/v8/gypfiles/v8.gyp node.gyp \
97
              config.gypi
98
	$(PYTHON) tools/gyp_node.py -f make
99 100

config.gypi: configure
101
	$(error Missing or stale $@, please run ./$<)
102

103
.PHONY: install
104
install: all ## Installs node into $PREFIX (default=/usr/local).
105
	$(PYTHON) tools/install.py $@ '$(DESTDIR)' '$(PREFIX)'
106

107
.PHONY: uninstall
108
uninstall: ## Uninstalls node from $PREFIX (default=/usr/local).
109
	$(PYTHON) tools/install.py $@ '$(DESTDIR)' '$(PREFIX)'
110

111
.PHONY: clean
112
clean: ## Remove build artifacts.
113
	$(RM) -r out/Makefile $(NODE_EXE) $(NODE_G_EXE) out/$(BUILDTYPE)/$(NODE_EXE) \
114
		out/$(BUILDTYPE)/node.exp
115 116 117 118
	@if [ -d out ]; then find out/ -name '*.o' -o -name '*.a' -o -name '*.d' | xargs $(RM) -r; fi
	$(RM) -r node_modules
	@if [ -d deps/icu ]; then echo deleting deps/icu; $(RM) -r deps/icu; fi
	$(RM) test.tap
119 120 121 122
	# Next one is legacy remove this at some point
	$(RM) -r test/tmp*
	$(RM) -r test/.tmp*
	$(MAKE) test-addons-clean
123

124
.PHONY: distclean
125
distclean:
126 127 128 129 130 131 132 133 134
	$(RM) -r out
	$(RM) config.gypi icu_config.gypi config_fips.gypi
	$(RM) config.mk
	$(RM) -r $(NODE_EXE) $(NODE_G_EXE)
	$(RM) -r node_modules
	$(RM) -r deps/icu
	$(RM) -r deps/icu4c*.tgz deps/icu4c*.zip deps/icu-tmp
	$(RM) $(BINARYTAR).* $(TARBALL).*
	$(RM) -r deps/v8/testing/gmock
135

136
.PHONY: check
137
check: test
138

139
.PHONY: coverage-clean
140 141 142 143 144
# Remove files generated by running coverage, put the non-instrumented lib back
# in place
coverage-clean:
	if [ -d lib_ ]; then $(RM) -r lib; mv lib_ lib; fi
	$(RM) -r node_modules
145
	$(RM) -r gcovr build
146
	$(RM) -r out/$(BUILDTYPE)/.coverage
147
	$(RM) -r .cov_tmp
148 149
	$(RM) out/$(BUILDTYPE)/obj.target/node/gen/*.gcda
	$(RM) out/$(BUILDTYPE)/obj.target/node/src/*.gcda
150
	$(RM) out/$(BUILDTYPE)/obj.target/node/src/tracing/*.gcda
151 152
	$(RM) out/$(BUILDTYPE)/obj.target/node/gen/*.gcno
	$(RM) out/$(BUILDTYPE)/obj.target/node/src/*.gcno
153 154 155
	$(RM) out/$(BUILDTYPE)/obj.target/node/src/tracing/*.gcno
	$(RM) out/$(BUILDTYPE)/obj.target/cctest/src/*.gcno
	$(RM) out/$(BUILDTYPE)/obj.target/cctest/test/cctest/*.gcno
156

157
.PHONY: coverage
158 159 160 161
# Build and test with code coverage reporting.  Leave the lib directory
# instrumented for any additional runs the user may want to make.
# For C++ coverage reporting, this needs to be run in conjunction with configure
#  --coverage.  html coverage reports will be created under coverage/
162
# Related CI job: node-test-commit-linux-coverage
163
coverage: coverage-test ## Run the tests and generate a coverage report.
164

165
.PHONY: coverage-build
166 167 168
coverage-build: all
	mkdir -p node_modules
	if [ ! -d node_modules/istanbul-merge ]; then \
169 170 171
		$(NODE) ./deps/npm install istanbul-merge --no-save --no-package-lock; fi
	if [ ! -d node_modules/nyc ]; then \
		$(NODE) ./deps/npm install nyc --no-save --no-package-lock; fi
172
	if [ ! -d gcovr ]; then git clone -b 3.4 --depth=1 \
173
		--single-branch git://github.com/gcovr/gcovr.git; fi
174 175
	if [ ! -d build ]; then git clone --depth=1 \
		--single-branch https://github.com/nodejs/build.git; fi
176 177
	if [ ! -f gcovr/scripts/gcovr.orig ]; then \
		(cd gcovr && patch -N -p1 < \
178
		"$(CURDIR)/build/jenkins/scripts/coverage/gcovr-patches-3.4.diff"); fi
179 180
	if [ -d lib_ ]; then $(RM) -r lib; mv lib_ lib; fi
	mv lib lib_
181
	$(NODE) ./node_modules/.bin/nyc instrument --extension .js --extension .mjs lib_/ lib/
182 183
	$(MAKE)

184
.PHONY: coverage-test
185 186 187
coverage-test: coverage-build
	$(RM) -r out/$(BUILDTYPE)/.coverage
	$(RM) -r .cov_tmp
188 189
	$(RM) out/$(BUILDTYPE)/obj.target/node/gen/*.gcda
	$(RM) out/$(BUILDTYPE)/obj.target/node/src/*.gcda
190
	$(RM) out/$(BUILDTYPE)/obj.target/node/src/tracing/*.gcda
191 192 193
	$(RM) out/$(BUILDTYPE)/obj.target/node_lib/gen/*.gcda
	$(RM) out/$(BUILDTYPE)/obj.target/node_lib/src/*.gcda
	$(RM) out/$(BUILDTYPE)/obj.target/node_lib/src/tracing/*.gcda
194 195 196 197 198 199 200
	-$(MAKE) $(COVTESTS)
	mv lib lib__
	mv lib_ lib
	mkdir -p coverage .cov_tmp
	$(NODE) ./node_modules/.bin/istanbul-merge --out \
		.cov_tmp/libcov.json 'out/Release/.coverage/coverage-*.json'
	(cd lib && .$(NODE) ../node_modules/.bin/nyc report \
201
		--temp-directory "$(CURDIR)/.cov_tmp" \
202 203
		--report-dir "../coverage")
	-(cd out && "../gcovr/scripts/gcovr" --gcov-exclude='.*deps' \
204
		--gcov-exclude='.*usr' -v -r Release/obj.target \
205 206
		--html --html-detail -o ../coverage/cxxcoverage.html \
		--gcov-executable="$(GCOV)")
207 208 209 210 211 212 213 214 215
	mv lib lib_
	mv lib__ lib
	@echo -n "Javascript coverage %: "
	@grep -B1 Lines coverage/index.html | head -n1 \
		| sed 's/<[^>]*>//g'| sed 's/ //g'
	@echo -n "C++ coverage %: "
	@grep -A3 Lines coverage/cxxcoverage.html | grep style  \
		| sed 's/<[^>]*>//g'| sed 's/ //g'

216 217
.PHONY: cctest
# Runs the C++ tests using the built `cctest` executable.
218
cctest: all
219 220
	@out/$(BUILDTYPE)/$@ --gtest_filter=$(GTEST_FILTER)

221
.PHONY: list-gtests
222 223 224 225 226
list-gtests:
ifeq (,$(wildcard out/$(BUILDTYPE)/cctest))
	$(error Please run 'make cctest' first)
endif
	@out/$(BUILDTYPE)/cctest --gtest_list_tests
227

228 229 230 231
.PHONY: v8
# Related CI job: node-test-commit-v8-linux
# Rebuilds deps/v8 as a git tree, pulls its third-party dependencies, and
# builds it.
232
v8:
233
	tools/make-v8.sh $(V8_ARCH).$(BUILDTYPE_LOWER) $(V8_BUILD_OPTIONS)
234

235 236 237 238 239 240 241 242 243 244 245
.PHONY: jstest
jstest: build-addons build-addons-napi ## Runs addon tests and JS tests
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=release \
		$(CI_JS_SUITES) \
		$(CI_NATIVE_SUITES)

.PHONY: test
# This does not run tests of third-party libraries inside deps.
test: all ## Runs default tests, linters, and builds docs.
	# Build the addons before running the tests so the test results
	# can be displayed together
246 247
	$(MAKE) -s build-addons
	$(MAKE) -s build-addons-napi
248
	$(MAKE) -s test-doc
249
	$(MAKE) -s cctest
250
	$(MAKE) -s jstest
251

252 253 254 255
.PHONY: test-only
test-only: all  ## For a quick test, does not run linter or build docs.
	# Build the addons before running the tests so the test results
	# can be displayed together
256
	$(MAKE) build-addons
257
	$(MAKE) build-addons-napi
258
	$(MAKE) cctest
259
	$(MAKE) jstest
260

261
# Used by `make coverage-test`
262
test-cov: all
263 264
	# Build the addons before running the tests so the test results
	# can be displayed together
265 266 267
	$(MAKE) build-addons
	$(MAKE) build-addons-napi
	# $(MAKE) cctest
268
	$(MAKE) jstest
269
	$(MAKE) lint
270

271
test-parallel: all
272
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=release parallel
273

274
test-valgrind: all
275
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=release --valgrind sequential parallel message
276

277
test-check-deopts: all
278
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=release --check-deopts parallel sequential
279

280 281 282 283 284 285 286 287
benchmark/misc/function_call/build/Release/binding.node: all \
		benchmark/misc/function_call/binding.cc \
		benchmark/misc/function_call/binding.gyp
	$(NODE) deps/npm/node_modules/node-gyp/bin/node-gyp rebuild \
		--python="$(PYTHON)" \
		--directory="$(shell pwd)/benchmark/misc/function_call" \
		--nodedir="$(shell pwd)"

288 289 290 291
# Implicitly depends on $(NODE_EXE).  We don't depend on it explicitly because
# it always triggers a rebuild due to it being a .PHONY rule.  See the comment
# near the build-addons rule for more background.
test/gc/build/Release/binding.node: test/gc/binding.cc test/gc/binding.gyp
292
	$(NODE) deps/npm/node_modules/node-gyp/bin/node-gyp rebuild \
293
		--python="$(PYTHON)" \
294
		--directory="$(shell pwd)/test/gc" \
295
		--nodedir="$(shell pwd)"
296

297 298 299 300 301 302 303
DOCBUILDSTAMP_PREREQS = tools/doc/addon-verify.js doc/api/addons.md

ifeq ($(OSTYPE),aix)
DOCBUILDSTAMP_PREREQS := $(DOCBUILDSTAMP_PREREQS) out/$(BUILDTYPE)/node.exp
endif

test/addons/.docbuildstamp: $(DOCBUILDSTAMP_PREREQS)
304
	$(RM) -r test/addons/??_*/
305
	[ -x $(NODE) ] && $(NODE) $< || node $<
306 307 308
	touch $@

ADDONS_BINDING_GYPS := \
309
	$(filter-out test/addons/??_*/binding.gyp, \
310 311
		$(wildcard test/addons/*/binding.gyp))

312 313
ADDONS_BINDING_SOURCES := \
	$(filter-out test/addons/??_*/*.cc, $(wildcard test/addons/*/*.cc)) \
314
	$(filter-out test/addons/??_*/*.h, $(wildcard test/addons/*/*.h))
315

316
# Implicitly depends on $(NODE_EXE), see the build-addons rule for rationale.
317 318
# Depends on node-gyp package.json so that build-addons is (re)executed when
# node-gyp is updated as part of an npm update.
319 320
test/addons/.buildstamp: config.gypi \
	deps/npm/node_modules/node-gyp/package.json \
321
	$(ADDONS_BINDING_GYPS) $(ADDONS_BINDING_SOURCES) \
322
	deps/uv/include/*.h deps/v8/include/*.h \
323
	src/node.h src/node_buffer.h src/node_object_wrap.h src/node_version.h \
324
	test/addons/.docbuildstamp
325 326
#	Cannot use $(wildcard test/addons/*/) here, it's evaluated before
#	embedded addons have been generated from the documentation.
327 328
#	Ignore folders without binding.gyp
#	(https://github.com/nodejs/node/issues/14843)
329
	@for dirname in test/addons/*/; do \
330 331
		if [ ! -f "$$PWD/$${dirname}binding.gyp" ]; then \
			continue; fi ; \
332 333 334
		printf "\nBuilding addon $$PWD/$$dirname\n" ; \
		env MAKEFLAGS="-j1" $(NODE) deps/npm/node_modules/node-gyp/bin/node-gyp \
		        --loglevel=$(LOGLEVEL) rebuild \
335
			--python="$(PYTHON)" \
336
			--directory="$$PWD/$$dirname" \
337
			--nodedir="$$PWD" || exit 1 ; \
338 339 340
	done
	touch $@

341
.PHONY: build-addons
342
# .buildstamp needs $(NODE_EXE) but cannot depend on it
343 344
# directly because it calls make recursively.  The parent make cannot know
# if the subprocess touched anything so it pessimistically assumes that
345
# .buildstamp is out of date and need a rebuild.
346
# Just goes to show that recursive make really is harmful...
347
# TODO(bnoordhuis) Force rebuild after gyp update.
348
build-addons: | $(NODE_EXE) test/addons/.buildstamp
349

350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
ADDONS_NAPI_BINDING_GYPS := \
	$(filter-out test/addons-napi/??_*/binding.gyp, \
		$(wildcard test/addons-napi/*/binding.gyp))

ADDONS_NAPI_BINDING_SOURCES := \
	$(filter-out test/addons-napi/??_*/*.cc, $(wildcard test/addons-napi/*/*.cc)) \
	$(filter-out test/addons-napi/??_*/*.h, $(wildcard test/addons-napi/*/*.h))

# Implicitly depends on $(NODE_EXE), see the build-addons-napi rule for rationale.
test/addons-napi/.buildstamp: config.gypi \
	deps/npm/node_modules/node-gyp/package.json \
	$(ADDONS_NAPI_BINDING_GYPS) $(ADDONS_NAPI_BINDING_SOURCES) \
	deps/uv/include/*.h deps/v8/include/*.h \
	src/node.h src/node_buffer.h src/node_object_wrap.h src/node_version.h \
	src/node_api.h src/node_api_types.h
#	Cannot use $(wildcard test/addons-napi/*/) here, it's evaluated before
#	embedded addons have been generated from the documentation.
367 368
#	Ignore folders without binding.gyp
#	(https://github.com/nodejs/node/issues/14843)
369
	@for dirname in test/addons-napi/*/; do \
370 371
		if [ ! -f "$$PWD/$${dirname}binding.gyp" ]; then \
			continue; fi ; \
372 373 374 375 376 377 378 379 380
		printf "\nBuilding addon $$PWD/$$dirname\n" ; \
		env MAKEFLAGS="-j1" $(NODE) deps/npm/node_modules/node-gyp/bin/node-gyp \
		        --loglevel=$(LOGLEVEL) rebuild \
			--python="$(PYTHON)" \
			--directory="$$PWD/$$dirname" \
			--nodedir="$$PWD" || exit 1 ; \
	done
	touch $@

381
.PHONY: build-addons-napi
382
# .buildstamp needs $(NODE_EXE) but cannot depend on it
383 384
# directly because it calls make recursively.  The parent make cannot know
# if the subprocess touched anything so it pessimistically assumes that
385
# .buildstamp is out of date and need a rebuild.
386 387
# Just goes to show that recursive make really is harmful...
# TODO(bnoordhuis) Force rebuild after gyp or node-gyp update.
388
build-addons-napi: | $(NODE_EXE) test/addons-napi/.buildstamp
389

390
.PHONY: clear-stalled
391 392 393 394 395 396 397 398
clear-stalled:
	# Clean up any leftover processes but don't error if found.
	ps awwx | grep Release/node | grep -v grep | cat
	@PS_OUT=`ps awwx | grep Release/node | grep -v grep | awk '{print $$1}'`; \
	if [ "$${PS_OUT}" ]; then \
		echo $${PS_OUT} | xargs kill; \
	fi

399
.PHONY: test-gc
400
test-gc: all test/gc/build/Release/binding.node
401
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=release gc
402

403
.PHONY: test-gc-clean
404 405 406 407
test-gc-clean:
	$(RM) -r test/gc/build

test-build: | all build-addons build-addons-napi
408

409 410
test-build-addons-napi: all build-addons-napi

411
.PHONY: test-all
412
test-all: test-build test/gc/build/Release/binding.node ## Run everything in test/.
413
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=debug,release
414

415
test-all-valgrind: test-build
416
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=debug,release --valgrind
417

418 419 420
CI_NATIVE_SUITES ?= addons addons-napi
CI_JS_SUITES ?= default
CI_DOC := doctool
421

422
.PHONY: test-ci-native
423
# Build and test addons without building anything else
424
# Related CI job: node-test-commit-arm-fanned
425
test-ci-native: LOGLEVEL := info
426
test-ci-native: | test/addons/.buildstamp test/addons-napi/.buildstamp
427 428 429 430
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \
		--mode=release --flaky-tests=$(FLAKY_TESTS) \
		$(TEST_CI_ARGS) $(CI_NATIVE_SUITES)

431
.PHONY: test-ci-js
432
# This target should not use a native compiler at all
433
# Related CI job: node-test-commit-arm-fanned
434
test-ci-js: | clear-stalled
435 436
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \
		--mode=release --flaky-tests=$(FLAKY_TESTS) \
437
		$(TEST_CI_ARGS) $(CI_JS_SUITES)
438 439 440
	# Clean up any leftover processes, error if found.
	ps awwx | grep Release/node | grep -v grep | cat
	@PS_OUT=`ps awwx | grep Release/node | grep -v grep | awk '{print $$1}'`; \
441
	if [ "$${PS_OUT}" ]; then \
442
		echo $${PS_OUT} | xargs kill; exit 1; \
443
	fi
444

445 446
.PHONY: test-ci
# Related CI jobs: most CI tests, excluding node-test-commit-arm-fanned
447
test-ci: LOGLEVEL := info
448
test-ci: | clear-stalled build-addons build-addons-napi doc-only
449
	out/Release/cctest --gtest_output=tap:cctest.tap
450 451
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \
		--mode=release --flaky-tests=$(FLAKY_TESTS) \
452
		$(TEST_CI_ARGS) $(CI_JS_SUITES) $(CI_NATIVE_SUITES) $(CI_DOC)
453 454 455
	# Clean up any leftover processes, error if found.
	ps awwx | grep Release/node | grep -v grep | cat
	@PS_OUT=`ps awwx | grep Release/node | grep -v grep | awk '{print $$1}'`; \
456
	if [ "$${PS_OUT}" ]; then \
457
		echo $${PS_OUT} | xargs kill; exit 1; \
458
	fi
459

460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
.PHONY: build-ci
# Prepare the build for running the tests.
# Related CI jobs: most CI tests, excluding node-test-commit-arm-fanned
build-ci:
	$(PYTHON) ./configure $(CONFIG_FLAGS)
	$(MAKE)

.PHONY: run-ci
# Run by CI tests, exceptions:
# - node-test-commit-arm-fanned (Raspberry Pis), where the binaries are
#   cross-compiled, then transferred elsewhere to run different subsets
#   of tests. See `test-ci-native` and `test-ci-js`.
# - node-test-commit-linux-coverage: where the build and the tests need
#   to be instrumented, see `coverage`.
run-ci: build-ci
	$(MAKE) test-ci

477
test-release: test-build
478
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=release
479

480
test-debug: test-build
481
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=debug
482

483
test-message: test-build
484
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) message
485

486
test-simple: | cctest  # Depends on 'all'.
487
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) parallel sequential
488

489
test-pummel: all
490
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) pummel
491

492
test-internet: all
493
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) internet
494

495 496 497 498
test-node-inspect: $(NODE_EXE)
	USE_EMBEDDED_NODE_INSPECT=1 $(NODE) tools/test-npm-package \
		--install deps/node-inspect test

499
test-tick-processor: all
500
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) tick-processor
501

502 503
.PHONY: test-hash-seed
# Verifies the hash seed used by V8 for hashing is random.
504 505 506
test-hash-seed: all
	$(NODE) test/pummel/test-hash-seed.js

507 508
.PHONY: test-doc
test-doc: doc-only ## Builds, lints, and verifies the docs.
509
	$(MAKE) lint
510
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) $(CI_DOC)
511

512
test-known-issues: all
513
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) known_issues
514

515
# Related CI job: node-test-npm
516
test-npm: $(NODE_EXE) ## Run the npm test suite on deps/npm.
517
	$(NODE) tools/test-npm-package --install --logfile=test-npm.tap deps/npm test-node
518

519 520 521
test-npm-publish: $(NODE_EXE)
	npm_package_config_publishtest=true $(NODE) deps/npm/test/run.js

522
.PHONY: test-addons-napi
523
test-addons-napi: test-build-addons-napi
524
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=release addons-napi
525

526
.PHONY: test-addons-napi-clean
527 528 529 530
test-addons-napi-clean:
	$(RM) -r test/addons-napi/*/build
	$(RM) test/addons-napi/.buildstamp

531
.PHONY: test-addons
532
test-addons: test-build test-addons-napi
533
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=release addons
534

535
.PHONY: test-addons-clean
536
test-addons-clean:
537 538
	$(RM) -r test/addons/??_*/
	$(RM) -r test/addons/*/build
539
	$(RM) test/addons/.buildstamp test/addons/.docbuildstamp
540
	$(MAKE) test-addons-napi-clean
541

542 543
test-timers:
	$(MAKE) --directory=tools faketime
544
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=release timers
545 546 547 548

test-timers-clean:
	$(MAKE) --directory=tools clean

549
test-async-hooks:
550
	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=release async-hooks
551

552 553 554 555
test-with-async-hooks:
	$(MAKE) build-addons
	$(MAKE) build-addons-napi
	$(MAKE) cctest
556
	NODE_TEST_WITH_ASYNC_HOOKS=1 $(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=release \
557 558 559
		$(CI_JS_SUITES) \
		$(CI_NATIVE_SUITES)

560

561 562 563 564
.PHONY: test-v8
.PHONY: test-v8-all
.PHONY: test-v8-benchmarks
.PHONY: test-v8-intl
565
ifneq ("","$(wildcard deps/v8/tools/run-tests.py)")
566
# Related CI job: node-test-commit-v8-linux
567
test-v8: v8  ## Runs the V8 test suite on deps/v8.
568 569 570 571
	deps/v8/tools/run-tests.py --gn --arch=$(V8_ARCH) \
        --mode=$(BUILDTYPE_LOWER) $(V8_TEST_OPTIONS) \
				mjsunit cctest debugger inspector message preparser \
	      $(TAP_V8)
572 573
	@echo Testing hash seed
	$(MAKE) test-hash-seed
574

575
test-v8-intl: v8
576 577
	deps/v8/tools/run-tests.py --gn --arch=$(V8_ARCH) \
        --mode=$(BUILDTYPE_LOWER) intl \
578 579
        $(TAP_V8_INTL)

580
test-v8-benchmarks: v8
581 582 583
	deps/v8/tools/run-tests.py --gn --arch=$(V8_ARCH) --mode=$(BUILDTYPE_LOWER) \
        benchmarks \
	      $(TAP_V8_BENCHMARKS)
584 585

test-v8-all: test-v8 test-v8-intl test-v8-benchmarks
586
# runs all v8 tests
587 588 589 590 591 592
else
test-v8 test-v8-intl test-v8-benchmarks test-v8-all:
	@echo "Testing v8 is not available through the source tarball."
	@echo "Use the git repo instead:" \
		"$ git clone https://github.com/nodejs/node.git"
endif
593

594 595 596 597 598
# Google Analytics ID used for tracking API docs page views, empty
# DOCS_ANALYTICS means no tracking scripts will be included in the
# generated .html files
DOCS_ANALYTICS ?=

599
apidoc_dirs = out/doc out/doc/api out/doc/api/assets
600
apidoc_sources = $(wildcard doc/api/*.md)
601 602
apidocs_html = $(addprefix out/,$(apidoc_sources:.md=.html))
apidocs_json = $(addprefix out/,$(apidoc_sources:.md=.json))
603

604
apiassets = $(subst api_assets,api/assets,$(addprefix out/,$(wildcard doc/api_assets/*)))
605

606 607
.PHONY: doc-only
doc-only: $(apidoc_dirs) $(apiassets)  ## Builds the docs with the local or the global Node.js binary.
608 609 610 611 612
# If it's a source tarball, assets are already in doc/api/assets,
# no need to install anything, we have already copied the docs over
	if [ ! -d doc/api/assets ]; then \
		$(MAKE) tools/doc/node_modules/js-yaml/package.json; \
	fi;
613
	@$(MAKE) $(apidocs_html) $(apidocs_json)
614

615
.PHONY: doc
616
doc: $(NODE_EXE) doc-only
617

618 619 620 621 622 623 624 625
out/doc:
	mkdir -p $@

# If it's a source tarball, doc/api already contains the generated docs.
# Just copy everything under doc/api over.
out/doc/api: doc/api
	mkdir -p $@
	cp -r doc/api out/doc
626

627 628 629 630 631 632
# If it's a source tarball, assets are already in doc/api/assets
out/doc/api/assets:
	mkdir -p $@
	if [ -d doc/api/assets ]; then cp -r doc/api/assets out/doc/api; fi;

# If it's not a source tarball, we need to copy assets from doc/api_assets
633 634
out/doc/api/assets/%: doc/api_assets/% out/doc/api/assets
	@cp $< $@
635

636 637 638 639 640
# Use -e to double check in case it's a broken link
# Use $(PWD) so we can cd to anywhere before calling this
available-node = \
  if [ -x $(PWD)/$(NODE) ] && [ -e $(PWD)/$(NODE) ]; then \
		$(PWD)/$(NODE) $(1); \
641
	elif [ -x `which node` ] && [ -e `which node` ] && [ `which node` ]; then \
642 643 644 645 646 647 648 649 650 651
		`which node` $(1); \
	else \
		echo "No available node, cannot run \"node $(1)\""; \
		exit 1; \
	fi;

run-npm-install = $(PWD)/$(NPM) install --production

tools/doc/node_modules/js-yaml/package.json:
	cd tools/doc && $(call available-node,$(run-npm-install))
652

653
gen-json = tools/doc/generate.js --format=json $< > $@
654 655 656 657
gen-html = tools/doc/generate.js --node-version=$(FULLVERSION) --format=html \
			--template=doc/template.html --analytics=$(DOCS_ANALYTICS) $< > $@

out/doc/api/%.json: doc/api/%.md
658
	$(call available-node, $(gen-json))
659

660
out/doc/api/%.html: doc/api/%.md
661
	$(call available-node, $(gen-html))
662

663
.PHONY: docopen
664 665
docopen: $(apidocs_html)
	@$(PYTHON) -mwebbrowser file://$(PWD)/out/doc/api/all.html
666

667
.PHONY: docclean
668
docclean:
669
	$(RM) -r out/doc
670

671 672
RAWVER=$(shell $(PYTHON) tools/getnodeversion.py)
VERSION=v$(RAWVER)
673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708

# For nightly builds, you must set DISTTYPE to "nightly", "next-nightly" or
# "custom". For the nightly and next-nightly case, you need to set DATESTRING
# and COMMIT in order to properly name the build.
# For the rc case you need to set CUSTOMTAG to an appropriate CUSTOMTAG number

ifndef DISTTYPE
DISTTYPE=release
endif
ifeq ($(DISTTYPE),release)
FULLVERSION=$(VERSION)
else # ifeq ($(DISTTYPE),release)
ifeq ($(DISTTYPE),custom)
ifndef CUSTOMTAG
$(error CUSTOMTAG is not set for DISTTYPE=custom)
endif # ifndef CUSTOMTAG
TAG=$(CUSTOMTAG)
else # ifeq ($(DISTTYPE),custom)
ifndef DATESTRING
$(error DATESTRING is not set for nightly)
endif # ifndef DATESTRING
ifndef COMMIT
$(error COMMIT is not set for nightly)
endif # ifndef COMMIT
ifneq ($(DISTTYPE),nightly)
ifneq ($(DISTTYPE),next-nightly)
$(error DISTTYPE is not release, custom, nightly or next-nightly)
endif # ifneq ($(DISTTYPE),next-nightly)
endif # ifneq ($(DISTTYPE),nightly)
TAG=$(DISTTYPE)$(DATESTRING)$(COMMIT)
endif # ifeq ($(DISTTYPE),custom)
FULLVERSION=$(VERSION)-$(TAG)
endif # ifeq ($(DISTTYPE),release)

DISTTYPEDIR ?= $(DISTTYPE)
RELEASE=$(shell sed -ne 's/\#define NODE_VERSION_IS_RELEASE \([01]\)/\1/p' src/node_version.h)
709
PLATFORM=$(shell uname | tr '[:upper:]' '[:lower:]')
710 711
NPMVERSION=v$(shell cat deps/npm/package.json | grep '"version"' | sed 's/^[^:]*: "\([^"]*\)",.*/\1/')

712 713
UNAME_M=$(shell uname -m)
ifeq ($(findstring x86_64,$(UNAME_M)),x86_64)
714 715
DESTCPU ?= x64
else
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
ifeq ($(findstring ppc64,$(UNAME_M)),ppc64)
DESTCPU ?= ppc64
else
ifeq ($(findstring ppc,$(UNAME_M)),ppc)
DESTCPU ?= ppc
else
ifeq ($(findstring s390x,$(UNAME_M)),s390x)
DESTCPU ?= s390x
else
ifeq ($(findstring s390,$(UNAME_M)),s390)
DESTCPU ?= s390
else
ifeq ($(findstring arm,$(UNAME_M)),arm)
DESTCPU ?= arm
else
ifeq ($(findstring aarch64,$(UNAME_M)),aarch64)
DESTCPU ?= aarch64
else
ifeq ($(findstring powerpc,$(shell uname -p)),powerpc)
DESTCPU ?= ppc64
else
737
DESTCPU ?= x86
738
endif
739 740 741 742 743 744 745
endif
endif
endif
endif
endif
endif
endif
746 747 748
ifeq ($(DESTCPU),x64)
ARCH=x64
else
749 750 751
ifeq ($(DESTCPU),arm)
ARCH=arm
else
752 753 754
ifeq ($(DESTCPU),aarch64)
ARCH=arm64
else
755 756 757 758 759 760
ifeq ($(DESTCPU),ppc64)
ARCH=ppc64
else
ifeq ($(DESTCPU),ppc)
ARCH=ppc
else
761 762 763 764 765 766
ifeq ($(DESTCPU),s390)
ARCH=s390
else
ifeq ($(DESTCPU),s390x)
ARCH=s390x
else
767 768
ARCH=x86
endif
769 770
endif
endif
771 772
endif
endif
773 774
endif
endif
775 776 777 778 779 780 781 782

# node and v8 use different arch names (e.g. node 'x86' vs v8 'ia32').
# pass the proper v8 arch name to $V8_ARCH based on user-specified $DESTCPU.
ifeq ($(DESTCPU),x86)
V8_ARCH=ia32
else
V8_ARCH ?= $(DESTCPU)

783 784
endif

785 786 787 788 789 790 791
# enforce "x86" over "ia32" as the generally accepted way of referring to 32-bit intel
ifeq ($(ARCH),ia32)
override ARCH=x86
endif
ifeq ($(DESTCPU),ia32)
override DESTCPU=x86
endif
792

793 794
TARNAME=node-$(FULLVERSION)
TARBALL=$(TARNAME).tar
795 796 797 798
# Custom user-specified variation, use it directly
ifdef VARIATION
BINARYNAME=$(TARNAME)-$(PLATFORM)-$(ARCH)-$(VARIATION)
else
799
BINARYNAME=$(TARNAME)-$(PLATFORM)-$(ARCH)
800
endif
801 802 803
BINARYTAR=$(BINARYNAME).tar
# OSX doesn't have xz installed by default, http://macpkg.sourceforge.net/
XZ=$(shell which xz > /dev/null 2>&1; echo $$?)
804
XZ_COMPRESSION ?= 9e
805
PKG=$(TARNAME).pkg
806
MACOSOUTDIR=out/macos
807

808
.PHONY: release-only
809
release-only:
810 811
	@if [ "$(DISTTYPE)" != "nightly" ] && [ "$(DISTTYPE)" != "next-nightly" ] && \
		`grep -q REPLACEME doc/api/*.md`; then \
812
		echo 'Please update REPLACEME in Added: tags in doc/api/*.md (See doc/releases.md)' ; \
813 814
		exit 1 ; \
	fi
815 816 817 818 819
	@if [ "$(DISTTYPE)" != "nightly" ] && [ "$(DISTTYPE)" != "next-nightly" ] && \
		`grep -q DEP00XX doc/api/deprecations.md`; then \
		echo 'Please update DEP00XX in doc/api/deprecations.md (See doc/releases.md)' ; \
		exit 1 ; \
	fi
820 821 822
	@if [ "$(shell git status --porcelain | egrep -v '^\?\? ')" = "" ]; then \
		exit 0 ; \
	else \
823
		echo "" >&2 ; \
824 825 826 827 828 829 830
		echo "The git repository is not clean." >&2 ; \
		echo "Please commit changes before building release tarball." >&2 ; \
		echo "" >&2 ; \
		git status --porcelain | egrep -v '^\?\?' >&2 ; \
		echo "" >&2 ; \
		exit 1 ; \
	fi
831
	@if [ "$(DISTTYPE)" != "release" -o "$(RELEASE)" = "1" ]; then \
832 833
		exit 0; \
	else \
834
		echo "" >&2 ; \
835
		echo "#NODE_VERSION_IS_RELEASE is set to $(RELEASE)." >&2 ; \
836 837
		echo "Did you remember to update src/node_version.h?" >&2 ; \
		echo "" >&2 ; \
838 839 840 841
		exit 1 ; \
	fi

$(PKG): release-only
842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
	$(RM) -r $(MACOSOUTDIR)
	mkdir -p $(MACOSOUTDIR)/installer/productbuild
	cat tools/macos-installer/productbuild/distribution.xml.tmpl  \
		| sed -E "s/\\{nodeversion\\}/$(FULLVERSION)/g" \
		| sed -E "s/\\{npmversion\\}/$(NPMVERSION)/g" \
	>$(MACOSOUTDIR)/installer/productbuild/distribution.xml ; \

	@for dirname in tools/macos-installer/productbuild/Resources/*/; do \
		lang=$$(basename $$dirname) ; \
		mkdir -p $(MACOSOUTDIR)/installer/productbuild/Resources/$$lang ; \
		printf "Found localization directory $$dirname\n" ; \
		cat $$dirname/welcome.html.tmpl  \
			| sed -E "s/\\{nodeversion\\}/$(FULLVERSION)/g" \
			| sed -E "s/\\{npmversion\\}/$(NPMVERSION)/g"  \
		>$(MACOSOUTDIR)/installer/productbuild/Resources/$$lang/welcome.html ; \
		cat $$dirname/conclusion.html.tmpl  \
			| sed -E "s/\\{nodeversion\\}/$(FULLVERSION)/g" \
			| sed -E "s/\\{npmversion\\}/$(NPMVERSION)/g"  \
		>$(MACOSOUTDIR)/installer/productbuild/Resources/$$lang/conclusion.html ; \
	done
862 863 864 865 866
	$(PYTHON) ./configure \
		--dest-cpu=x64 \
		--tag=$(TAG) \
		--release-urlbase=$(RELEASE_URLBASE) \
		$(CONFIG_FLAGS) $(BUILD_RELEASE_FLAGS)
867 868
	$(MAKE) install V=$(V) DESTDIR=$(MACOSOUTDIR)/dist/node
	SIGN="$(CODESIGN_CERT)" PKGDIR="$(MACOSOUTDIR)/dist/node/usr/local" bash \
869
		tools/osx-codesign.sh
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889
	mkdir -p $(MACOSOUTDIR)/dist/npm/usr/local/lib/node_modules
	mkdir -p $(MACOSOUTDIR)/pkgs
	mv $(MACOSOUTDIR)/dist/node/usr/local/lib/node_modules/npm \
		$(MACOSOUTDIR)/dist/npm/usr/local/lib/node_modules
	unlink $(MACOSOUTDIR)/dist/node/usr/local/bin/npm
	unlink $(MACOSOUTDIR)/dist/node/usr/local/bin/npx
	$(NODE) tools/license2rtf.js < LICENSE > \
		$(MACOSOUTDIR)/installer/productbuild/Resources/license.rtf
	cp doc/osx_installer_logo.png $(MACOSOUTDIR)/installer/productbuild/Resources
	pkgbuild --version $(FULLVERSION) \
		--identifier org.nodejs.node.pkg \
		--root $(MACOSOUTDIR)/dist/node $(MACOSOUTDIR)/pkgs/node-$(FULLVERSION).pkg
	pkgbuild --version $(NPMVERSION) \
		--identifier org.nodejs.npm.pkg \
		--root $(MACOSOUTDIR)/dist/npm \
		--scripts ./tools/macos-installer/pkgbuild/npm/scripts \
			$(MACOSOUTDIR)/pkgs/npm-$(NPMVERSION).pkg
	productbuild --distribution $(MACOSOUTDIR)/installer/productbuild/distribution.xml \
		--resources $(MACOSOUTDIR)/installer/productbuild/Resources \
		--package-path $(MACOSOUTDIR)/pkgs ./$(PKG)
890
	SIGN="$(PRODUCTSIGN_CERT)" PKG="$(PKG)" bash tools/osx-productsign.sh
891

892 893
.PHONY: pkg
# Builds the macOS installer for releases.
894 895
pkg: $(PKG)

896
# Note: this is strictly for release builds on release machines only.
897 898
pkg-upload: pkg
	ssh $(STAGINGSERVER) "mkdir -p nodejs/$(DISTTYPEDIR)/$(FULLVERSION)"
899 900 901
	chmod 664 $(TARNAME).pkg
	scp -p $(TARNAME).pkg $(STAGINGSERVER):nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME).pkg
	ssh $(STAGINGSERVER) "touch nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME).pkg.done"
902 903 904

$(TARBALL): release-only $(NODE_EXE) doc
	git checkout-index -a -f --prefix=$(TARNAME)/
905
	mkdir -p $(TARNAME)/doc/api
906
	cp doc/node.1 $(TARNAME)/doc/node.1
907
	cp -r out/doc/api/* $(TARNAME)/doc/api/
908 909 910 911 912 913 914 915 916 917 918 919 920
	$(RM) -r $(TARNAME)/.editorconfig
	$(RM) -r $(TARNAME)/.git*
	$(RM) -r $(TARNAME)/.mailmap
	$(RM) -r $(TARNAME)/deps/openssl/openssl/demos
	$(RM) -r $(TARNAME)/deps/openssl/openssl/doc
	$(RM) -r $(TARNAME)/deps/openssl/openssl/test
	$(RM) -r $(TARNAME)/deps/uv/docs
	$(RM) -r $(TARNAME)/deps/uv/samples
	$(RM) -r $(TARNAME)/deps/uv/test
	$(RM) -r $(TARNAME)/deps/v8/samples
	$(RM) -r $(TARNAME)/deps/v8/test
	$(RM) -r $(TARNAME)/deps/v8/tools/profviz
	$(RM) -r $(TARNAME)/deps/v8/tools/run-tests.py
921
	$(RM) -r $(TARNAME)/deps/zlib/contrib # too big, unused
922
	$(RM) -r $(TARNAME)/doc/images # too big
923
	$(RM) -r $(TARNAME)/test*.tap
924 925 926 927 928 929 930 931 932
	$(RM) -r $(TARNAME)/tools/cpplint.py
	$(RM) -r $(TARNAME)/tools/eslint-rules
	$(RM) -r $(TARNAME)/tools/license-builder.sh
	$(RM) -r $(TARNAME)/tools/node_modules
	$(RM) -r $(TARNAME)/tools/osx-*
	$(RM) -r $(TARNAME)/tools/osx-pkg.pmdoc
	$(RM) -r $(TARNAME)/tools/pkgsrc
	$(RM) -r $(TARNAME)/tools/remark-cli
	$(RM) -r $(TARNAME)/tools/remark-preset-lint-node
933 934
	find $(TARNAME)/ -name ".eslint*" -maxdepth 2 | xargs $(RM)
	find $(TARNAME)/ -type l | xargs $(RM) # annoying on windows
935
	tar -cf $(TARNAME).tar $(TARNAME)
936
	$(RM) -r $(TARNAME)
937 938 939 940
	gzip -c -f -9 $(TARNAME).tar > $(TARNAME).tar.gz
ifeq ($(XZ), 0)
	xz -c -f -$(XZ_COMPRESSION) $(TARNAME).tar > $(TARNAME).tar.xz
endif
941
	$(RM) $(TARNAME).tar
942

943
.PHONY: tar
944
tar: $(TARBALL) ## Create a source tarball.
945

946
# Note: this is strictly for release builds on release machines only.
947 948
tar-upload: tar
	ssh $(STAGINGSERVER) "mkdir -p nodejs/$(DISTTYPEDIR)/$(FULLVERSION)"
949 950 951
	chmod 664 $(TARNAME).tar.gz
	scp -p $(TARNAME).tar.gz $(STAGINGSERVER):nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME).tar.gz
	ssh $(STAGINGSERVER) "touch nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME).tar.gz.done"
952
ifeq ($(XZ), 0)
953 954 955
	chmod 664 $(TARNAME).tar.xz
	scp -p $(TARNAME).tar.xz $(STAGINGSERVER):nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME).tar.xz
	ssh $(STAGINGSERVER) "touch nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME).tar.xz.done"
956 957
endif

958
# Note: this is strictly for release builds on release machines only.
959
doc-upload: doc
960
	ssh $(STAGINGSERVER) "mkdir -p nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/docs/"
961
	chmod -R ug=rw-x+X,o=r+X out/doc/
962
	scp -pr out/doc/* $(STAGINGSERVER):nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/docs/
963 964
	ssh $(STAGINGSERVER) "touch nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/docs.done"

965
.PHONY: $(TARBALL)-headers
966
$(TARBALL)-headers: release-only
967 968 969 970 971 972 973
	$(PYTHON) ./configure \
		--prefix=/ \
		--dest-cpu=$(DESTCPU) \
		--tag=$(TAG) \
		--release-urlbase=$(RELEASE_URLBASE) \
		$(CONFIG_FLAGS) $(BUILD_RELEASE_FLAGS)
	HEADERS_ONLY=1 $(PYTHON) tools/install.py install '$(TARNAME)' '/'
974
	find $(TARNAME)/ -type l | xargs $(RM)
975
	tar -cf $(TARNAME)-headers.tar $(TARNAME)
976
	$(RM) -r $(TARNAME)
977 978 979 980
	gzip -c -f -9 $(TARNAME)-headers.tar > $(TARNAME)-headers.tar.gz
ifeq ($(XZ), 0)
	xz -c -f -$(XZ_COMPRESSION) $(TARNAME)-headers.tar > $(TARNAME)-headers.tar.xz
endif
981
	$(RM) $(TARNAME)-headers.tar
982

983
tar-headers: $(TARBALL)-headers ## Build the node header tarball.
984 985 986 987 988 989 990 991 992 993 994 995

tar-headers-upload: tar-headers
	ssh $(STAGINGSERVER) "mkdir -p nodejs/$(DISTTYPEDIR)/$(FULLVERSION)"
	chmod 664 $(TARNAME)-headers.tar.gz
	scp -p $(TARNAME)-headers.tar.gz $(STAGINGSERVER):nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME)-headers.tar.gz
	ssh $(STAGINGSERVER) "touch nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME)-headers.tar.gz.done"
ifeq ($(XZ), 0)
	chmod 664 $(TARNAME)-headers.tar.xz
	scp -p $(TARNAME)-headers.tar.xz $(STAGINGSERVER):nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME)-headers.tar.xz
	ssh $(STAGINGSERVER) "touch nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME)-headers.tar.xz.done"
endif

996
$(BINARYTAR): release-only
997 998
	$(RM) -r $(BINARYNAME)
	$(RM) -r out/deps out/Release
999 1000 1001 1002 1003 1004
	$(PYTHON) ./configure \
		--prefix=/ \
		--dest-cpu=$(DESTCPU) \
		--tag=$(TAG) \
		--release-urlbase=$(RELEASE_URLBASE) \
		$(CONFIG_FLAGS) $(BUILD_RELEASE_FLAGS)
1005 1006 1007
	$(MAKE) install DESTDIR=$(BINARYNAME) V=$(V) PORTABLE=1
	cp README.md $(BINARYNAME)
	cp LICENSE $(BINARYNAME)
1008
	cp CHANGELOG.md $(BINARYNAME)
1009 1010 1011
ifeq ($(OSTYPE),darwin)
	SIGN="$(CODESIGN_CERT)" PKGDIR="$(BINARYNAME)" bash tools/osx-codesign.sh
endif
1012
	tar -cf $(BINARYNAME).tar $(BINARYNAME)
1013
	$(RM) -r $(BINARYNAME)
1014 1015 1016 1017
	gzip -c -f -9 $(BINARYNAME).tar > $(BINARYNAME).tar.gz
ifeq ($(XZ), 0)
	xz -c -f -$(XZ_COMPRESSION) $(BINARYNAME).tar > $(BINARYNAME).tar.xz
endif
1018
	$(RM) $(BINARYNAME).tar
1019

1020 1021
.PHONY: binary
# This requires NODE_VERSION_IS_RELEASE defined as 1 in src/node_version.h.
1022
binary: $(BINARYTAR) ## Build release binary tarballs.
1023

1024
# Note: this is strictly for release builds on release machines only.
1025 1026
binary-upload: binary
	ssh $(STAGINGSERVER) "mkdir -p nodejs/$(DISTTYPEDIR)/$(FULLVERSION)"
1027 1028 1029
	chmod 664 $(TARNAME)-$(OSTYPE)-$(ARCH).tar.gz
	scp -p $(TARNAME)-$(OSTYPE)-$(ARCH).tar.gz $(STAGINGSERVER):nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME)-$(OSTYPE)-$(ARCH).tar.gz
	ssh $(STAGINGSERVER) "touch nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME)-$(OSTYPE)-$(ARCH).tar.gz.done"
1030
ifeq ($(XZ), 0)
1031 1032 1033
	chmod 664 $(TARNAME)-$(OSTYPE)-$(ARCH).tar.xz
	scp -p $(TARNAME)-$(OSTYPE)-$(ARCH).tar.xz $(STAGINGSERVER):nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME)-$(OSTYPE)-$(ARCH).tar.xz
	ssh $(STAGINGSERVER) "touch nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME)-$(OSTYPE)-$(ARCH).tar.xz.done"
1034
endif
1035

1036 1037 1038
.PHONY: bench-all
bench-all:
	@echo "Please use benchmark/run.js or benchmark/compare.js to run the benchmarks."
1039

1040 1041 1042
.PHONY: bench
bench:
	@echo "Please use benchmark/run.js or benchmark/compare.js to run the benchmarks."
1043

1044
.PHONY: lint-md-clean
1045 1046 1047 1048 1049
lint-md-clean:
	$(RM) -r tools/remark-cli/node_modules
	$(RM) -r tools/remark-preset-lint-node/node_modules
	$(RM) tools/.*mdlintstamp

1050 1051 1052 1053 1054 1055 1056 1057
tools/remark-cli/node_modules: tools/remark-cli/package.json
	@echo "Markdown linter: installing remark-cli into tools/"
	@cd tools/remark-cli && $(call available-node,$(run-npm-install))

tools/remark-preset-lint-node/node_modules: \
	tools/remark-preset-lint-node/package.json
	@echo "Markdown linter: installing remark-preset-lint-node into tools/"
	@cd tools/remark-preset-lint-node && $(call available-node,$(run-npm-install))
1058

1059 1060 1061 1062 1063
.PHONY: lint-md-build
lint-md-build: tools/remark-cli/node_modules \
	tools/remark-preset-lint-node/node_modules

.PHONY: lint-md
1064 1065
ifneq ("","$(wildcard tools/remark-cli/node_modules/)")

1066 1067 1068 1069
LINT_MD_DOC_FILES = $(shell ls doc/**/*.md)
run-lint-doc-md = tools/remark-cli/cli.js -q -f $(LINT_MD_DOC_FILES)
# Lint all changed markdown files under doc/
tools/.docmdlintstamp: $(LINT_MD_DOC_FILES)
1070
	@echo "Running Markdown linter on docs..."
1071
	@$(call available-node,$(run-lint-doc-md))
1072 1073
	@touch $@

1074 1075 1076 1077 1078 1079 1080
LINT_MD_TARGETS = src lib benchmark tools/doc tools/icu
LINT_MD_ROOT_DOCS := $(wildcard *.md)
LINT_MD_MISC_FILES := $(shell find $(LINT_MD_TARGETS) -type f \
  -not -path '*node_modules*' -name '*.md') $(LINT_MD_ROOT_DOCS)
run-lint-misc-md = tools/remark-cli/cli.js -q -f $(LINT_MD_MISC_FILES)
# Lint other changed markdown files maintained by us
tools/.miscmdlintstamp: $(LINT_MD_MISC_FILES)
1081
	@echo "Running Markdown linter on misc docs..."
1082
	@$(call available-node,$(run-lint-misc-md))
1083 1084 1085 1086
	@touch $@

tools/.mdlintstamp: tools/.miscmdlintstamp tools/.docmdlintstamp

1087
# Lints the markdown documents maintained by us in the codebase.
1088 1089 1090 1091 1092 1093 1094
lint-md: | tools/.mdlintstamp
else
lint-md:
	@echo "The markdown linter is not installed."
	@echo "To install (requires internet access) run: $ make lint-md-build"
endif

1095 1096 1097 1098 1099
LINT_JS_TARGETS = .eslintrc.js benchmark doc lib test tools

run-lint-js = tools/node_modules/eslint/bin/eslint.js --cache \
	--ext=.js,.mjs,.md $(LINT_JS_TARGETS) --ignore-pattern '!.eslintrc.js'
run-lint-js-fix = $(run-lint-js) --fix
1100

1101
.PHONY: lint-js-fix
1102
lint-js-fix:
1103
	@$(call available-node,$(run-lint-js-fix))
1104

1105 1106 1107
.PHONY: lint-js
# Note that on the CI `lint-js-ci` is run instead.
# Lints the JavaScript code with eslint.
1108
lint-js:
1109
	@echo "Running JS linter..."
1110
	@$(call available-node,$(run-lint-js))
1111

1112 1113 1114
jslint: lint-js
	@echo "Please use lint-js instead of jslint"

1115 1116 1117 1118 1119
run-lint-js-ci = tools/lint-js.js $(PARALLEL_ARGS) -f tap -o test-eslint.tap \
		$(LINT_JS_TARGETS)

.PHONY: lint-js-ci
# On the CI the output is emitted in the TAP format.
1120
lint-js-ci:
1121
	@echo "Running JS linter..."
1122
	@$(call available-node,$(run-lint-js-ci))
1123 1124 1125 1126

jslint-ci: lint-js-ci
	@echo "Please use lint-js-ci instead of jslint-ci"

1127 1128
LINT_CPP_ADDON_DOC_FILES_GLOB = test/addons/??_*/*.cc test/addons/??_*/*.h
LINT_CPP_ADDON_DOC_FILES = $(wildcard $(LINT_CPP_ADDON_DOC_FILES_GLOB))
1129 1130 1131 1132
LINT_CPP_EXCLUDE ?=
LINT_CPP_EXCLUDE += src/node_root_certs.h
LINT_CPP_EXCLUDE += $(LINT_CPP_ADDON_DOC_FILES)
LINT_CPP_EXCLUDE += $(wildcard test/addons-napi/??_*/*.cc test/addons-napi/??_*/*.h)
1133
# These files were copied more or less verbatim from V8.
1134
LINT_CPP_EXCLUDE += src/tracing/trace_event.h src/tracing/trace_event_common.h
1135

1136 1137
LINT_CPP_FILES = $(filter-out $(LINT_CPP_EXCLUDE), $(wildcard \
	benchmark/misc/function_call/binding.cc \
1138 1139 1140
	src/*.c \
	src/*.cc \
	src/*.h \
1141 1142 1143
	src/*/*.c \
	src/*/*.cc \
	src/*/*.h \
1144 1145
	test/addons/*/*.cc \
	test/addons/*/*.h \
1146 1147
	test/cctest/*.cc \
	test/cctest/*.h \
1148 1149 1150
	test/addons-napi/*/*.cc \
	test/addons-napi/*/*.h \
	test/gc/binding.cc \
1151 1152 1153
	tools/icu/*.cc \
	tools/icu/*.h \
	))
1154

1155 1156 1157 1158
# Code blocks don't have newline at the end,
# and the actual filename is generated so it won't match header guards
ADDON_DOC_LINT_FLAGS=-whitespace/ending_newline,-build/header_guard

1159 1160
.PHONY: lint-cpp
# Lints the C++ code with cpplint.py and check-imports.py.
1161 1162 1163
lint-cpp: tools/.cpplintstamp

tools/.cpplintstamp: $(LINT_CPP_FILES)
1164
	@echo "Running C++ linter..."
1165
	@$(PYTHON) tools/cpplint.py $?
1166
	@$(PYTHON) tools/check-imports.py
1167 1168 1169 1170
	@touch $@

lint-addon-docs: test/addons/.docbuildstamp
	@echo "Running C++ linter on addon docs..."
1171
	@$(PYTHON) tools/cpplint.py --filter=$(ADDON_DOC_LINT_FLAGS) $(LINT_CPP_ADDON_DOC_FILES_GLOB)
1172

1173 1174 1175
cpplint: lint-cpp
	@echo "Please use lint-cpp instead of cpplint"

1176 1177 1178
.PHONY: lint
.PHONY: lint-ci
ifneq ("","$(wildcard tools/node_modules/eslint/)")
1179
lint: ## Run JS, C++, MD and doc linters.
1180
	@EXIT_STATUS=0 ; \
1181 1182 1183
	$(MAKE) lint-js || EXIT_STATUS=$$? ; \
	$(MAKE) lint-cpp || EXIT_STATUS=$$? ; \
	$(MAKE) lint-addon-docs || EXIT_STATUS=$$? ; \
1184
	$(MAKE) lint-md || EXIT_STATUS=$$? ; \
1185
	exit $$EXIT_STATUS
1186
CONFLICT_RE=^>>>>>>> [0-9A-Fa-f]+|^<<<<<<< [A-Za-z]+
1187 1188

# Related CI job: node-test-linter
1189
lint-ci: lint-js-ci lint-cpp lint-md lint-addon-docs
1190 1191 1192 1193 1194 1195 1196 1197
	@if ! ( grep -IEqrs "$(CONFLICT_RE)" benchmark deps doc lib src test tools ) \
		&& ! ( find . -maxdepth 1 -type f | xargs grep -IEqs "$(CONFLICT_RE)" ); then \
		exit 0 ; \
	else \
		echo "" >&2 ; \
		echo "Conflict marker detected in one or more files. Please fix them first." >&2 ; \
		exit 1 ; \
	fi
1198 1199 1200 1201 1202 1203 1204 1205
else
lint:
	@echo "Linting is not available through the source tarball."
	@echo "Use the git repo instead:" \
		"$ git clone https://github.com/nodejs/node.git"

lint-ci: lint
endif
1206

1207
.PHONY: lint-clean
1208 1209 1210
lint-clean:
	$(RM) tools/.*lintstamp
	$(RM) .eslintcache