1-include config.mk
2
3BUILDTYPE ?= Release
4PYTHON ?= python3
5DESTDIR ?=
6SIGN ?=
7PREFIX ?= /usr/local
8FLAKY_TESTS ?= run
9TEST_CI_ARGS ?=
10STAGINGSERVER ?= node-www
11LOGLEVEL ?= silent
12OSTYPE := $(shell uname -s | tr '[:upper:]' '[:lower:]')
13ifeq ($(findstring os/390,$OSTYPE),os/390)
14OSTYPE ?= os390
15endif
16ARCHTYPE := $(shell uname -m | tr '[:upper:]' '[:lower:]')
17COVTESTS ?= test-cov
18COV_SKIP_TESTS ?= core_line_numbers.js,testFinalizer.js,test_function/test.js
19GTEST_FILTER ?= "*"
20GNUMAKEFLAGS += --no-print-directory
21GCOV ?= gcov
22PWD = $(CURDIR)
23BUILD_WITH ?= make
24FIND ?= find
25
26ifdef JOBS
27	PARALLEL_ARGS = -j $(JOBS)
28else
29	PARALLEL_ARGS =
30endif
31
32ifdef ENABLE_V8_TAP
33	TAP_V8 := --junitout $(PWD)/v8-tap.xml
34	TAP_V8_INTL := --junitout $(PWD)/v8-intl-tap.xml
35	TAP_V8_BENCHMARKS := --junitout $(PWD)/v8-benchmarks-tap.xml
36define convert_to_junit
37	@true
38endef
39endif
40
41ifdef ENABLE_CONVERT_V8_JSON_TO_XML
42	TAP_V8_JSON := $(PWD)/v8-tap.json
43	TAP_V8_INTL_JSON := $(PWD)/v8-intl-tap.json
44	TAP_V8_BENCHMARKS_JSON := $(PWD)/v8-benchmarks-tap.json
45
46	# By default, the V8's JSON test output only includes the tests which have
47	# failed. We use --slow-tests-cutoff to ensure that all tests are present
48	# in the output, including those which pass.
49	TAP_V8 := --json-test-results $(TAP_V8_JSON) --slow-tests-cutoff 1000000
50	TAP_V8_INTL := --json-test-results $(TAP_V8_INTL_JSON) --slow-tests-cutoff 1000000
51	TAP_V8_BENCHMARKS := --json-test-results $(TAP_V8_BENCHMARKS_JSON) --slow-tests-cutoff 1000000
52
53define convert_to_junit
54	export PATH="$(NO_BIN_OVERRIDE_PATH)" && \
55		$(PYTHON) tools/v8-json-to-junit.py < $(1) > $(1:.json=.xml)
56endef
57endif
58
59V8_TEST_OPTIONS = $(V8_EXTRA_TEST_OPTIONS)
60ifdef DISABLE_V8_I18N
61	V8_BUILD_OPTIONS += i18nsupport=off
62endif
63# V8 build and test toolchains are not currently compatible with Python 3.
64# config.mk may have prepended a symlink for `python` to PATH which we need
65# to undo before calling V8's tools.
66OVERRIDE_BIN_DIR=$(dir $(abspath $(lastword $(MAKEFILE_LIST))))out/tools/bin
67NO_BIN_OVERRIDE_PATH=$(subst $() $(),:,$(filter-out $(OVERRIDE_BIN_DIR),$(subst :, ,$(PATH))))
68
69ifeq ($(OSTYPE), darwin)
70	GCOV = xcrun llvm-cov gcov
71endif
72
73BUILDTYPE_LOWER := $(shell echo $(BUILDTYPE) | tr '[:upper:]' '[:lower:]')
74
75# Determine EXEEXT
76EXEEXT := $(shell $(PYTHON) -c \
77		"import sys; print('.exe' if sys.platform == 'win32' else '')")
78
79NODE_EXE = node$(EXEEXT)
80NODE ?= ./$(NODE_EXE)
81NODE_G_EXE = node_g$(EXEEXT)
82NPM ?= ./deps/npm/bin/npm-cli.js
83
84# Flags for packaging.
85BUILD_DOWNLOAD_FLAGS ?= --download=all
86BUILD_INTL_FLAGS ?= --with-intl=full-icu
87BUILD_RELEASE_FLAGS ?= $(BUILD_DOWNLOAD_FLAGS) $(BUILD_INTL_FLAGS)
88
89# Default to quiet/pretty builds.
90# To do verbose builds, run `make V=1` or set the V environment variable.
91V ?= 0
92
93# Use -e to double check in case it's a broken link
94# Use $(PWD) so we can cd to anywhere before calling this
95available-node = \
96	if [ -x $(PWD)/$(NODE) ] && [ -e $(PWD)/$(NODE) ]; then \
97		$(PWD)/$(NODE) $(1); \
98	elif [ -x `command -v node` ] && [ -e `command -v node` ] && [ `command -v node` ]; then \
99		`command -v node` $(1); \
100	else \
101		echo "No available node, cannot run \"node $(1)\""; \
102		exit 1; \
103	fi;
104
105.PHONY: all
106# BUILDTYPE=Debug builds both release and debug builds. If you want to compile
107# just the debug build, run `make -C out BUILDTYPE=Debug` instead.
108ifeq ($(BUILDTYPE),Release)
109all: $(NODE_EXE) ## Default target, builds node in out/Release/node.
110else
111all: $(NODE_EXE) $(NODE_G_EXE)
112endif
113
114.PHONY: help
115# To add a target to the help, add a double comment (##) on the target line.
116help: ## Print help for targets with comments.
117	@printf "For more targets and info see the comments in the Makefile.\n\n"
118	@grep -E '^[[:alnum:]._-]+:.*?## .*$$' Makefile | sort | \
119		awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}'
120
121# The .PHONY is needed to ensure that we recursively use the out/Makefile
122# to check for changes.
123.PHONY: $(NODE_EXE) $(NODE_G_EXE)
124
125# The -r/-L check stops it recreating the link if it is already in place,
126# otherwise $(NODE_EXE) being a .PHONY target means it is always re-run.
127# Without the check there is a race condition between the link being deleted
128# and recreated which can break the addons build when running test-ci
129# See comments on the build-addons target for some more info
130ifeq ($(BUILD_WITH), make)
131$(NODE_EXE): build_type:=Release
132$(NODE_G_EXE): build_type:=Debug
133$(NODE_EXE) $(NODE_G_EXE): config.gypi out/Makefile
134	$(MAKE) -C out BUILDTYPE=${build_type} V=$(V)
135	if [ ! -r $@ ] || [ ! -L $@ ]; then \
136	  ln -fs out/${build_type}/$(NODE_EXE) $@; fi
137else
138ifeq ($(BUILD_WITH), ninja)
139NINJA ?= ninja
140ifeq ($(V),1)
141	NINJA_ARGS := $(NINJA_ARGS) -v
142endif
143ifdef JOBS
144	NINJA_ARGS := $(NINJA_ARGS) -j$(JOBS)
145else
146	NINJA_ARGS := $(NINJA_ARGS) $(filter -j%,$(MAKEFLAGS))
147endif
148$(NODE_EXE): config.gypi out/Release/build.ninja
149	$(NINJA) -C out/Release $(NINJA_ARGS)
150	if [ ! -r $@ ] || [ ! -L $@ ]; then ln -fs out/Release/$(NODE_EXE) $@; fi
151
152$(NODE_G_EXE): config.gypi out/Debug/build.ninja
153	$(NINJA) -C out/Debug $(NINJA_ARGS)
154	if [ ! -r $@ ] || [ ! -L $@ ]; then ln -fs out/Debug/$(NODE_EXE) $@; fi
155else
156$(NODE_EXE) $(NODE_G_EXE):
157	$(warning This Makefile currently only supports building with 'make' or 'ninja')
158endif
159endif
160
161
162ifeq ($(BUILDTYPE),Debug)
163CONFIG_FLAGS += --debug
164endif
165
166.PHONY: with-code-cache
167.PHONY: test-code-cache
168with-code-cache test-code-cache:
169	$(warning '$@' target is a noop)
170
171out/Makefile: config.gypi common.gypi node.gyp \
172	deps/uv/uv.gyp deps/llhttp/llhttp.gyp deps/zlib/zlib.gyp \
173	deps/simdutf/simdutf.gyp deps/ada/ada.gyp \
174	tools/v8_gypfiles/toolchain.gypi tools/v8_gypfiles/features.gypi \
175	tools/v8_gypfiles/inspector.gypi tools/v8_gypfiles/v8.gyp
176	$(PYTHON) tools/gyp_node.py -f make
177
178# node_version.h is listed because the N-API version is taken from there
179# and included in config.gypi
180config.gypi: configure configure.py src/node_version.h
181	@if [ -x config.status ]; then \
182		export PATH="$(NO_BIN_OVERRIDE_PATH)" && ./config.status; \
183	else \
184		echo Missing or stale $@, please run ./$<; \
185		exit 1; \
186	fi
187
188.PHONY: install
189install: all ## Installs node into $PREFIX (default=/usr/local).
190	$(PYTHON) tools/install.py $@ '$(DESTDIR)' '$(PREFIX)'
191
192.PHONY: uninstall
193uninstall: ## Uninstalls node from $PREFIX (default=/usr/local).
194	$(PYTHON) tools/install.py $@ '$(DESTDIR)' '$(PREFIX)'
195
196.PHONY: clean
197.NOTPARALLEL: clean
198clean: ## Remove build artifacts.
199	$(RM) -r out/Makefile $(NODE_EXE) $(NODE_G_EXE) out/$(BUILDTYPE)/$(NODE_EXE) \
200		out/$(BUILDTYPE)/node.exp
201	@if [ -d out ]; then $(FIND) out/ -name '*.o' -o -name '*.a' -o -name '*.d' | xargs $(RM) -r; fi
202	$(RM) -r node_modules
203	@if [ -d deps/icu ]; then echo deleting deps/icu; $(RM) -r deps/icu; fi
204	$(RM) test.tap
205	$(MAKE) testclean
206	$(MAKE) test-addons-clean
207	$(MAKE) bench-addons-clean
208
209.PHONY: testclean
210.NOTPARALLEL: testclean
211testclean:
212# Next one is legacy remove this at some point
213	$(RM) -r test/tmp*
214	$(RM) -r test/.tmp*
215
216.PHONY: distclean
217.NOTPARALLEL: distclean
218distclean:
219	$(RM) -r out
220	$(RM) config.gypi icu_config.gypi
221	$(RM) config.mk
222	$(RM) -r $(NODE_EXE) $(NODE_G_EXE)
223	$(RM) -r node_modules
224	$(RM) -r deps/icu
225	$(RM) -r deps/icu4c*.tgz deps/icu4c*.zip deps/icu-tmp
226	$(RM) $(BINARYTAR).* $(TARBALL).*
227
228.PHONY: check
229check: test
230
231.PHONY: coverage-clean
232.NOTPARALLEL: coverage-clean
233# Remove files generated by running coverage, put the non-instrumented lib back
234# in place
235coverage-clean:
236	$(RM) -r node_modules
237	$(RM) -r gcovr
238	$(RM) -r coverage/tmp
239	$(FIND) out/$(BUILDTYPE)/obj.target \( -name "*.gcda" -o -name "*.gcno" \) \
240		-type f -exec $(RM) {} \;
241
242.PHONY: coverage
243# Build and test with code coverage reporting. HTML coverage reports will be
244# created under coverage/. For C++ coverage reporting, this needs to be run
245# in conjunction with configure --coverage.
246# Related CI job: node-test-commit-linux-coverage-daily
247coverage: coverage-test ## Run the tests and generate a coverage report.
248
249.PHONY: coverage-build
250coverage-build: all
251	-$(MAKE) coverage-build-js
252	if [ ! -d gcovr ]; then $(PYTHON) -m pip install -t gcovr gcovr==4.2; fi
253	$(MAKE)
254
255.PHONY: coverage-build-js
256coverage-build-js:
257	mkdir -p node_modules
258	if [ ! -d node_modules/c8 ]; then \
259		$(NODE) ./deps/npm install c8 --no-save --no-package-lock;\
260	fi
261
262.PHONY: coverage-test
263coverage-test: coverage-build
264	$(FIND) out/$(BUILDTYPE)/obj.target -name "*.gcda" -type f -exec $(RM) {} \;
265	-NODE_V8_COVERAGE=coverage/tmp \
266		TEST_CI_ARGS="$(TEST_CI_ARGS) --type=coverage" $(MAKE) $(COVTESTS)
267	$(MAKE) coverage-report-js
268	-(cd out && PYTHONPATH=../gcovr $(PYTHON) -m gcovr \
269		--gcov-exclude='.*\b(deps|usr|out|cctest|embedding)\b' -v \
270		-r ../src/ --object-directory Release/obj.target \
271		--html --html-details -o ../coverage/cxxcoverage.html \
272		--gcov-executable="$(GCOV)")
273	@printf "Javascript coverage %%: "
274	@grep -B1 Lines coverage/index.html | head -n1 \
275		| sed 's/<[^>]*>//g'| sed 's/ //g'
276	@printf "C++ coverage %%: "
277	@grep -A3 Lines coverage/cxxcoverage.html | grep style  \
278		| sed 's/<[^>]*>//g'| sed 's/ //g'
279
280.PHONY: coverage-report-js
281coverage-report-js:
282	-$(MAKE) coverage-build-js
283	$(NODE) ./node_modules/.bin/c8 report
284
285.PHONY: cctest
286# Runs the C++ tests using the built `cctest` executable.
287cctest: all
288	@out/$(BUILDTYPE)/$@ --gtest_filter=$(GTEST_FILTER)
289	@out/$(BUILDTYPE)/embedtest "require('./test/embedding/test-embedding.js')"
290
291.PHONY: list-gtests
292list-gtests:
293ifeq (,$(wildcard out/$(BUILDTYPE)/cctest))
294	$(error Please run 'make cctest' first)
295endif
296	@out/$(BUILDTYPE)/cctest --gtest_list_tests
297
298.PHONY: v8
299# Related CI job: node-test-commit-v8-linux
300# Rebuilds deps/v8 as a git tree, pulls its third-party dependencies, and
301# builds it.
302v8:
303	export PATH="$(NO_BIN_OVERRIDE_PATH)" && \
304		tools/make-v8.sh $(V8_ARCH).$(BUILDTYPE_LOWER) $(V8_BUILD_OPTIONS)
305
306.PHONY: jstest
307jstest: build-addons build-js-native-api-tests build-node-api-tests ## Runs addon tests and JS tests
308	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=$(BUILDTYPE_LOWER) \
309		$(TEST_CI_ARGS) \
310		--skip-tests=$(CI_SKIP_TESTS) \
311		$(JS_SUITES) \
312		$(NATIVE_SUITES)
313
314.PHONY: tooltest
315tooltest:
316	@$(PYTHON) -m unittest discover -s ./test/tools
317
318.PHONY: coverage-run-js
319coverage-run-js:
320	$(RM) -r coverage/tmp
321	-NODE_V8_COVERAGE=coverage/tmp CI_SKIP_TESTS=$(COV_SKIP_TESTS) \
322					TEST_CI_ARGS="$(TEST_CI_ARGS) --type=coverage" $(MAKE) jstest
323	$(MAKE) coverage-report-js
324
325.PHONY: test
326# This does not run tests of third-party libraries inside deps.
327test: all ## Runs default tests, linters, and builds docs.
328	$(MAKE) -s tooltest
329	$(MAKE) -s test-doc
330	$(MAKE) -s build-addons
331	$(MAKE) -s build-js-native-api-tests
332	$(MAKE) -s build-node-api-tests
333	$(MAKE) -s cctest
334	$(MAKE) -s jstest
335
336.PHONY: test-only
337test-only: all  ## For a quick test, does not run linter or build docs.
338	$(MAKE) build-addons
339	$(MAKE) build-js-native-api-tests
340	$(MAKE) build-node-api-tests
341	$(MAKE) cctest
342	$(MAKE) jstest
343	$(MAKE) tooltest
344
345# Used by `make coverage-test`
346.PHONY: test-cov
347test-cov: all
348	$(MAKE) build-addons
349	$(MAKE) build-js-native-api-tests
350	$(MAKE) build-node-api-tests
351	$(MAKE) cctest
352	CI_SKIP_TESTS=$(COV_SKIP_TESTS) $(MAKE) jstest
353
354.PHONY: test-parallel
355test-parallel: all
356	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=$(BUILDTYPE_LOWER) parallel
357
358.PHONY: test-valgrind
359test-valgrind: all
360	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=$(BUILDTYPE_LOWER) --valgrind sequential parallel message
361
362.PHONY: test-check-deopts
363test-check-deopts: all
364	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=$(BUILDTYPE_LOWER) --check-deopts parallel sequential
365
366DOCBUILDSTAMP_PREREQS = tools/doc/addon-verify.mjs doc/api/addons.md
367
368ifeq ($(OSTYPE),aix)
369DOCBUILDSTAMP_PREREQS := $(DOCBUILDSTAMP_PREREQS) out/$(BUILDTYPE)/node.exp
370endif
371ifeq ($(OSTYPE),os400)
372DOCBUILDSTAMP_PREREQS := $(DOCBUILDSTAMP_PREREQS) out/$(BUILDTYPE)/node.exp
373endif
374
375node_use_openssl = $(call available-node,"-p" \
376			 "process.versions.openssl != undefined")
377test/addons/.docbuildstamp: $(DOCBUILDSTAMP_PREREQS) tools/doc/node_modules
378	@if [ "$(shell $(node_use_openssl))" != "true" ]; then \
379		echo "Skipping .docbuildstamp (no crypto)"; \
380	else \
381		$(RM) -r test/addons/??_*/; \
382		[ -x $(NODE) ] && $(NODE) $< || node $< ; \
383		[ $$? -eq 0 ] && touch $@; \
384	fi
385
386ADDONS_BINDING_GYPS := \
387	$(filter-out test/addons/??_*/binding.gyp, \
388		$(wildcard test/addons/*/binding.gyp))
389
390ADDONS_BINDING_SOURCES := \
391	$(filter-out test/addons/??_*/*.cc, $(wildcard test/addons/*/*.cc)) \
392	$(filter-out test/addons/??_*/*.h, $(wildcard test/addons/*/*.h))
393
394ADDONS_PREREQS := config.gypi \
395	deps/npm/node_modules/node-gyp/package.json tools/build-addons.mjs \
396	deps/uv/include/*.h deps/v8/include/*.h \
397	src/node.h src/node_buffer.h src/node_object_wrap.h src/node_version.h
398
399define run_build_addons
400env npm_config_loglevel=$(LOGLEVEL) npm_config_nodedir="$$PWD" \
401	npm_config_python="$(PYTHON)" $(NODE) "$$PWD/tools/build-addons.mjs" \
402	"$$PWD/deps/npm/node_modules/node-gyp/bin/node-gyp.js" \
403	$1
404touch $2
405endef
406
407# Implicitly depends on $(NODE_EXE), see the build-addons rule for rationale.
408# Depends on node-gyp package.json so that build-addons is (re)executed when
409# node-gyp is updated as part of an npm update.
410test/addons/.buildstamp: $(ADDONS_PREREQS) \
411	$(ADDONS_BINDING_GYPS) $(ADDONS_BINDING_SOURCES) \
412	test/addons/.docbuildstamp
413	@$(call run_build_addons,"$$PWD/test/addons",$@)
414
415.PHONY: build-addons
416# .buildstamp needs $(NODE_EXE) but cannot depend on it
417# directly because it calls make recursively.  The parent make cannot know
418# if the subprocess touched anything so it pessimistically assumes that
419# .buildstamp is out of date and need a rebuild.
420# Just goes to show that recursive make really is harmful...
421# TODO(bnoordhuis) Force rebuild after gyp update.
422build-addons: | $(NODE_EXE) test/addons/.buildstamp
423
424JS_NATIVE_API_BINDING_GYPS := \
425	$(filter-out test/js-native-api/??_*/binding.gyp, \
426		$(wildcard test/js-native-api/*/binding.gyp))
427
428JS_NATIVE_API_BINDING_SOURCES := \
429	$(filter-out test/js-native-api/??_*/*.c, $(wildcard test/js-native-api/*/*.c)) \
430	$(filter-out test/js-native-api/??_*/*.cc, $(wildcard test/js-native-api/*/*.cc)) \
431	$(filter-out test/js-native-api/??_*/*.h, $(wildcard test/js-native-api/*/*.h))
432
433# Implicitly depends on $(NODE_EXE), see the build-js-native-api-tests rule for rationale.
434test/js-native-api/.buildstamp: $(ADDONS_PREREQS) \
435	$(JS_NATIVE_API_BINDING_GYPS) $(JS_NATIVE_API_BINDING_SOURCES) \
436	src/node_api.h src/node_api_types.h src/js_native_api.h \
437	src/js_native_api_types.h src/js_native_api_v8.h src/js_native_api_v8_internals.h
438	@$(call run_build_addons,"$$PWD/test/js-native-api",$@)
439
440.PHONY: build-js-native-api-tests
441# .buildstamp needs $(NODE_EXE) but cannot depend on it
442# directly because it calls make recursively.  The parent make cannot know
443# if the subprocess touched anything so it pessimistically assumes that
444# .buildstamp is out of date and need a rebuild.
445# Just goes to show that recursive make really is harmful...
446# TODO(bnoordhuis) Force rebuild after gyp or node-gyp update.
447build-js-native-api-tests: | $(NODE_EXE) test/js-native-api/.buildstamp
448
449NODE_API_BINDING_GYPS := \
450	$(filter-out test/node-api/??_*/binding.gyp, \
451		$(wildcard test/node-api/*/binding.gyp))
452
453NODE_API_BINDING_SOURCES := \
454	$(filter-out test/node-api/??_*/*.c, $(wildcard test/node-api/*/*.c)) \
455	$(filter-out test/node-api/??_*/*.cc, $(wildcard test/node-api/*/*.cc)) \
456	$(filter-out test/node-api/??_*/*.h, $(wildcard test/node-api/*/*.h))
457
458# Implicitly depends on $(NODE_EXE), see the build-node-api-tests rule for rationale.
459test/node-api/.buildstamp: $(ADDONS_PREREQS) \
460	$(NODE_API_BINDING_GYPS) $(NODE_API_BINDING_SOURCES) \
461	src/node_api.h src/node_api_types.h src/js_native_api.h \
462	src/js_native_api_types.h src/js_native_api_v8.h src/js_native_api_v8_internals.h
463	@$(call run_build_addons,"$$PWD/test/node-api",$@)
464
465.PHONY: build-node-api-tests
466# .buildstamp needs $(NODE_EXE) but cannot depend on it
467# directly because it calls make recursively.  The parent make cannot know
468# if the subprocess touched anything so it pessimistically assumes that
469# .buildstamp is out of date and need a rebuild.
470# Just goes to show that recursive make really is harmful...
471# TODO(bnoordhuis) Force rebuild after gyp or node-gyp update.
472build-node-api-tests: | $(NODE_EXE) test/node-api/.buildstamp
473
474BENCHMARK_NAPI_BINDING_GYPS := $(wildcard benchmark/napi/*/binding.gyp)
475
476BENCHMARK_NAPI_BINDING_SOURCES := \
477	$(wildcard benchmark/napi/*/*.c) \
478	$(wildcard benchmark/napi/*/*.cc) \
479	$(wildcard benchmark/napi/*/*.h)
480
481benchmark/napi/.buildstamp: $(ADDONS_PREREQS) \
482	$(BENCHMARK_NAPI_BINDING_GYPS) $(BENCHMARK_NAPI_BINDING_SOURCES)
483	@$(call run_build_addons,"$$PWD/benchmark/napi",$@)
484
485.PHONY: clear-stalled
486clear-stalled:
487	$(info Clean up any leftover processes but don't error if found.)
488	ps awwx | grep Release/node | grep -v grep | cat
489	@PS_OUT=`ps awwx | grep Release/node | grep -v grep | awk '{print $$1}'`; \
490	if [ "$${PS_OUT}" ]; then \
491		echo $${PS_OUT} | xargs kill -9; \
492	fi
493
494.PHONY: test-build
495test-build: | all build-addons build-js-native-api-tests build-node-api-tests
496
497.PHONY: test-build-js-native-api
498test-build-js-native-api: all build-js-native-api-tests
499
500.PHONY: test-build-node-api
501test-build-node-api: all build-node-api-tests
502
503.PHONY: test-all
504test-all: test-build ## Run default tests with both Debug and Release builds.
505	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=debug,release
506
507.PHONY: test-all-valgrind
508test-all-valgrind: test-build
509	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=debug,release --valgrind
510
511.PHONY: test-all-suites
512test-all-suites: | clear-stalled test-build bench-addons-build doc-only ## Run all test suites.
513	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=$(BUILDTYPE_LOWER) test/*
514
515JS_SUITES ?= default
516NATIVE_SUITES ?= addons js-native-api node-api
517# CI_* variables should be kept synchronized with the ones in vcbuild.bat
518CI_NATIVE_SUITES ?= $(NATIVE_SUITES) benchmark
519CI_JS_SUITES ?= $(JS_SUITES) pummel
520ifeq ($(node_use_openssl), false)
521	CI_DOC := doctool
522else
523	CI_DOC =
524endif
525
526.PHONY: test-ci-native
527# Build and test addons without building anything else
528# Related CI job: node-test-commit-arm-fanned
529test-ci-native: LOGLEVEL := info
530test-ci-native: | benchmark/napi/.buildstamp test/addons/.buildstamp test/js-native-api/.buildstamp test/node-api/.buildstamp
531	$(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \
532		--mode=$(BUILDTYPE_LOWER) --flaky-tests=$(FLAKY_TESTS) \
533		$(TEST_CI_ARGS) $(CI_NATIVE_SUITES)
534
535.PHONY: test-ci-js
536# This target should not use a native compiler at all
537# Related CI job: node-test-commit-arm-fanned
538test-ci-js: | clear-stalled
539	$(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \
540		--mode=$(BUILDTYPE_LOWER) --flaky-tests=$(FLAKY_TESTS) \
541		$(TEST_CI_ARGS) $(CI_JS_SUITES)
542	$(info Clean up any leftover processes, error if found.)
543	ps awwx | grep Release/node | grep -v grep | cat
544	@PS_OUT=`ps awwx | grep Release/node | grep -v grep | awk '{print $$1}'`; \
545	if [ "$${PS_OUT}" ]; then \
546		echo $${PS_OUT} | xargs kill -9; exit 1; \
547	fi
548
549.PHONY: test-ci
550# Related CI jobs: most CI tests, excluding node-test-commit-arm-fanned
551test-ci: LOGLEVEL := info
552test-ci: | clear-stalled bench-addons-build build-addons build-js-native-api-tests build-node-api-tests doc-only
553	out/Release/cctest --gtest_output=xml:out/junit/cctest.xml
554	$(PYTHON) tools/test.py $(PARALLEL_ARGS) -p tap --logfile test.tap \
555		--mode=$(BUILDTYPE_LOWER) --flaky-tests=$(FLAKY_TESTS) \
556		$(TEST_CI_ARGS) $(CI_JS_SUITES) $(CI_NATIVE_SUITES) $(CI_DOC)
557	out/Release/embedtest 'require("./test/embedding/test-embedding.js")'
558	$(info Clean up any leftover processes, error if found.)
559	ps awwx | grep Release/node | grep -v grep | cat
560	@PS_OUT=`ps awwx | grep Release/node | grep -v grep | awk '{print $$1}'`; \
561	if [ "$${PS_OUT}" ]; then \
562		echo $${PS_OUT} | xargs kill -9; exit 1; \
563	fi
564
565.PHONY: build-ci
566# Prepare the build for running the tests.
567# Related CI jobs: most CI tests, excluding node-test-commit-arm-fanned
568build-ci:
569	$(PYTHON) ./configure --verbose $(CONFIG_FLAGS)
570	$(MAKE)
571
572.PHONY: run-ci
573# Run by CI tests, exceptions:
574# - node-test-commit-arm-fanned (Raspberry Pis), where the binaries are
575#   cross-compiled, then transferred elsewhere to run different subsets
576#   of tests. See `test-ci-native` and `test-ci-js`.
577# - node-test-commit-linux-coverage: where the build and the tests need
578#   to be instrumented, see `coverage`.
579#
580# Using -j1 as the sub target in `test-ci` already have internal parallelism.
581# Refs: https://github.com/nodejs/node/pull/23733
582run-ci: build-ci
583	$(MAKE) test-ci -j1
584
585.PHONY: test-release
586test-release: test-build
587	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=$(BUILDTYPE_LOWER)
588
589.PHONY: test-debug
590test-debug: test-build
591	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=debug
592
593.PHONY: test-message
594test-message: test-build
595	$(PYTHON) tools/test.py $(PARALLEL_ARGS) message
596
597.PHONY: test-wpt
598test-wpt: all
599	$(PYTHON) tools/test.py $(PARALLEL_ARGS) wpt
600
601.PHONY: test-wpt-report
602test-wpt-report:
603	$(RM) -r out/wpt
604	mkdir -p out/wpt
605	WPT_REPORT=1 $(PYTHON) tools/test.py --shell $(NODE) $(PARALLEL_ARGS) wpt
606
607.PHONY: test-simple
608test-simple: | cctest # Depends on 'all'.
609	$(PYTHON) tools/test.py $(PARALLEL_ARGS) parallel sequential
610
611.PHONY: test-pummel
612test-pummel: all
613	$(PYTHON) tools/test.py $(PARALLEL_ARGS) pummel
614
615.PHONY: test-internet
616test-internet: all
617	$(PYTHON) tools/test.py $(PARALLEL_ARGS) internet
618
619.PHONY: test-benchmark
620test-benchmark: | bench-addons-build
621	$(PYTHON) tools/test.py $(PARALLEL_ARGS) benchmark
622
623.PHONY: test-tick-processor
624test-tick-processor: all
625	$(PYTHON) tools/test.py $(PARALLEL_ARGS) tick-processor
626
627.PHONY: test-hash-seed
628# Verifies the hash seed used by V8 for hashing is random.
629test-hash-seed: all
630	$(NODE) test/pummel/test-hash-seed.js
631
632.PHONY: test-doc
633test-doc: doc-only lint-md ## Builds, lints, and verifies the docs.
634	@if [ "$(shell $(node_use_openssl))" != "true" ]; then \
635		echo "Skipping test-doc (no crypto)"; \
636	else \
637		$(PYTHON) tools/test.py $(PARALLEL_ARGS) doctool; \
638	fi
639
640.PHONY: test-doc-ci
641test-doc-ci: doc-only
642	$(PYTHON) tools/test.py --shell $(NODE) $(TEST_CI_ARGS) $(PARALLEL_ARGS) doctool
643
644.PHONY: test-known-issues
645test-known-issues: all
646	$(PYTHON) tools/test.py $(PARALLEL_ARGS) known_issues
647
648# Related CI job: node-test-npm
649.PHONY: test-npm
650test-npm: $(NODE_EXE) ## Run the npm test suite on deps/npm.
651	$(NODE) tools/test-npm-package --install --logfile=test-npm.tap deps/npm test
652
653.PHONY: test-npm-publish
654test-npm-publish: $(NODE_EXE)
655	npm_package_config_publishtest=true $(NODE) deps/npm/test/run.js
656
657.PHONY: test-js-native-api
658test-js-native-api: test-build-js-native-api
659	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=$(BUILDTYPE_LOWER) js-native-api
660
661.PHONY: test-js-native-api-clean
662.NOTPARALLEL: test-js-native-api-clean
663test-js-native-api-clean:
664	$(RM) -r test/js-native-api/*/build
665	$(RM) test/js-native-api/.buildstamp
666
667.PHONY: test-node-api
668test-node-api: test-build-node-api
669	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=$(BUILDTYPE_LOWER) node-api
670
671.PHONY: test-node-api-clean
672.NOTPARALLEL: test-node-api-clean
673test-node-api-clean:
674	$(RM) -r test/node-api/*/build
675	$(RM) test/node-api/.buildstamp
676
677.PHONY: test-addons
678test-addons: test-build test-js-native-api test-node-api
679	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=$(BUILDTYPE_LOWER) addons
680
681.PHONY: test-addons-clean
682.NOTPARALLEL: test-addons-clean
683test-addons-clean:
684	$(RM) -r test/addons/??_*/
685	$(RM) -r test/addons/*/build
686	$(RM) test/addons/.buildstamp test/addons/.docbuildstamp
687	$(MAKE) test-js-native-api-clean
688	$(MAKE) test-node-api-clean
689
690.PHONY: test-async-hooks
691test-async-hooks:
692	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=$(BUILDTYPE_LOWER) async-hooks
693
694.PHONY: test-with-async-hooks
695test-with-async-hooks:
696	$(MAKE) build-addons
697	$(MAKE) build-js-native-api-tests
698	$(MAKE) build-node-api-tests
699	$(MAKE) cctest
700	NODE_TEST_WITH_ASYNC_HOOKS=1 $(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=$(BUILDTYPE_LOWER) \
701		$(JS_SUITES) \
702		$(NATIVE_SUITES)
703
704
705.PHONY: test-v8
706.PHONY: test-v8-all
707.PHONY: test-v8-benchmarks
708.PHONY: test-v8-intl
709.PHONY: test-v8-updates
710ifneq ("","$(wildcard deps/v8/tools/run-tests.py)")
711# Related CI job: node-test-commit-v8-linux
712test-v8: v8  ## Runs the V8 test suite on deps/v8.
713	export PATH="$(NO_BIN_OVERRIDE_PATH)" && \
714		$(PYTHON) deps/v8/tools/run-tests.py --gn --arch=$(V8_ARCH) $(V8_TEST_OPTIONS) \
715				mjsunit cctest debugger inspector message preparser \
716				$(TAP_V8)
717	$(call convert_to_junit,$(TAP_V8_JSON))
718	$(info Testing hash seed)
719	$(MAKE) test-hash-seed
720
721test-v8-intl: v8
722	export PATH="$(NO_BIN_OVERRIDE_PATH)" && \
723		$(PYTHON) deps/v8/tools/run-tests.py --gn --arch=$(V8_ARCH) \
724				intl \
725				$(TAP_V8_INTL)
726	$(call convert_to_junit,$(TAP_V8_INTL_JSON))
727
728test-v8-benchmarks: v8
729	export PATH="$(NO_BIN_OVERRIDE_PATH)" && \
730		$(PYTHON) deps/v8/tools/run-tests.py --gn --arch=$(V8_ARCH) \
731				benchmarks \
732				$(TAP_V8_BENCHMARKS)
733	$(call convert_to_junit,$(TAP_V8_BENCHMARKS_JSON))
734
735test-v8-updates:
736	$(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=$(BUILDTYPE_LOWER) v8-updates
737
738test-v8-all: test-v8 test-v8-intl test-v8-benchmarks test-v8-updates
739# runs all v8 tests
740else
741test-v8 test-v8-intl test-v8-benchmarks test-v8-all:
742	$(warning Testing V8 is not available through the source tarball.)
743	$(warning Use the git repo instead: $$ git clone https://github.com/nodejs/node.git)
744endif
745
746apidoc_dirs = out/doc out/doc/api out/doc/api/assets
747apidoc_sources = $(wildcard doc/api/*.md)
748apidocs_html = $(addprefix out/,$(apidoc_sources:.md=.html))
749apidocs_json = $(addprefix out/,$(apidoc_sources:.md=.json))
750
751apiassets = $(subst api_assets,api/assets,$(addprefix out/,$(wildcard doc/api_assets/*)))
752
753tools/doc/node_modules: tools/doc/package.json
754	@if [ "$(shell $(node_use_openssl))" != "true" ]; then \
755		echo "Skipping tools/doc/node_modules (no crypto)"; \
756	else \
757		cd tools/doc && $(call available-node,$(run-npm-ci)) \
758	fi
759
760.PHONY: doc-only
761doc-only: tools/doc/node_modules \
762	$(apidoc_dirs) $(apiassets)  ## Builds the docs with the local or the global Node.js binary.
763	@if [ "$(shell $(node_use_openssl))" != "true" ]; then \
764		echo "Skipping doc-only (no crypto)"; \
765	else \
766		$(MAKE) out/doc/api/all.html out/doc/api/all.json out/doc/api/stability; \
767	fi
768
769.PHONY: doc
770doc: $(NODE_EXE) doc-only
771
772out/doc:
773	mkdir -p $@
774
775# If it's a source tarball, doc/api already contains the generated docs.
776# Just copy everything under doc/api over.
777out/doc/api: doc/api
778	mkdir -p $@
779	cp -r doc/api out/doc
780
781# If it's a source tarball, assets are already in doc/api/assets
782out/doc/api/assets:
783	mkdir -p $@
784	if [ -d doc/api/assets ]; then cp -r doc/api/assets out/doc/api; fi;
785
786# If it's not a source tarball, we need to copy assets from doc/api_assets
787out/doc/api/assets/%: doc/api_assets/% | out/doc/api/assets
788	@cp $< $@ ; $(RM) out/doc/api/assets/README.md
789
790
791run-npm-ci = $(PWD)/$(NPM) ci
792
793LINK_DATA = out/doc/apilinks.json
794VERSIONS_DATA = out/previous-doc-versions.json
795gen-api = tools/doc/generate.mjs --node-version=$(FULLVERSION) \
796		--apilinks=$(LINK_DATA) $< --output-directory=out/doc/api \
797		--versions-file=$(VERSIONS_DATA)
798gen-apilink = tools/doc/apilinks.mjs $(LINK_DATA) $(wildcard lib/*.js)
799
800$(LINK_DATA): $(wildcard lib/*.js) tools/doc/apilinks.mjs | out/doc
801	$(call available-node, $(gen-apilink))
802
803# Regenerate previous versions data if the current version changes
804$(VERSIONS_DATA): CHANGELOG.md src/node_version.h tools/doc/versions.mjs
805	$(call available-node, tools/doc/versions.mjs $@)
806
807node_use_icu = $(call available-node,"-p" "typeof Intl === 'object'")
808
809out/doc/api/%.json out/doc/api/%.html: doc/api/%.md tools/doc/generate.mjs \
810	tools/doc/markdown.mjs tools/doc/html.mjs tools/doc/json.mjs \
811	tools/doc/apilinks.mjs $(VERSIONS_DATA) | $(LINK_DATA) out/doc/api
812	@if [ "$(shell $(node_use_icu))" != "true" ]; then \
813		echo "Skipping documentation generation (no ICU)"; \
814	else \
815		$(call available-node, $(gen-api)) \
816	fi
817
818out/doc/api/all.html: $(apidocs_html) tools/doc/allhtml.mjs \
819	tools/doc/apilinks.mjs | out/doc/api
820	@if [ "$(shell $(node_use_icu))" != "true" ]; then \
821		echo "Skipping HTML single-page doc generation (no ICU)"; \
822	else \
823		$(call available-node, tools/doc/allhtml.mjs) \
824	fi
825
826out/doc/api/all.json: $(apidocs_json) tools/doc/alljson.mjs | out/doc/api
827	@if [ "$(shell $(node_use_icu))" != "true" ]; then \
828		echo "Skipping JSON single-file generation (no ICU)"; \
829	else \
830		$(call available-node, tools/doc/alljson.mjs) \
831	fi
832
833.PHONY: out/doc/api/stability
834out/doc/api/stability: out/doc/api/all.json tools/doc/stability.mjs | out/doc/api
835	@if [ "$(shell $(node_use_icu))" != "true" ]; then \
836		echo "Skipping stability indicator generation (no ICU)"; \
837	else \
838		$(call available-node, tools/doc/stability.mjs) \
839	fi
840
841.PHONY: docopen
842docopen: out/doc/api/all.html
843	@$(PYTHON) -mwebbrowser file://$(abspath $<)
844
845.PHONY: docserve
846docserve: $(apidocs_html) $(apiassets)
847	@$(PYTHON) -m http.server 8000 --bind 127.0.0.1 --directory out/doc/api
848
849.PHONY: docclean
850.NOTPARALLEL: docclean
851docclean:
852	$(RM) -r out/doc
853	$(RM) "$(VERSIONS_DATA)"
854
855RAWVER=$(shell $(PYTHON) tools/getnodeversion.py)
856VERSION=v$(RAWVER)
857CHANGELOG=doc/changelogs/CHANGELOG_V$(firstword $(subst ., ,$(RAWVER))).md
858
859# For nightly builds, you must set DISTTYPE to "nightly", "next-nightly" or
860# "custom". For the nightly and next-nightly case, you need to set DATESTRING
861# and COMMIT in order to properly name the build.
862# For the rc case you need to set CUSTOMTAG to an appropriate CUSTOMTAG number
863
864ifndef DISTTYPE
865DISTTYPE=release
866endif
867ifeq ($(DISTTYPE),release)
868FULLVERSION=$(VERSION)
869else # ifeq ($(DISTTYPE),release)
870ifeq ($(DISTTYPE),custom)
871ifndef CUSTOMTAG
872$(error CUSTOMTAG is not set for DISTTYPE=custom)
873endif # ifndef CUSTOMTAG
874TAG=$(CUSTOMTAG)
875else # ifeq ($(DISTTYPE),custom)
876ifndef DATESTRING
877$(error DATESTRING is not set for nightly)
878endif # ifndef DATESTRING
879ifndef COMMIT
880$(error COMMIT is not set for nightly)
881endif # ifndef COMMIT
882ifneq ($(DISTTYPE),nightly)
883ifneq ($(DISTTYPE),next-nightly)
884$(error DISTTYPE is not release, custom, nightly or next-nightly)
885endif # ifneq ($(DISTTYPE),next-nightly)
886endif # ifneq ($(DISTTYPE),nightly)
887TAG=$(DISTTYPE)$(DATESTRING)$(COMMIT)
888endif # ifeq ($(DISTTYPE),custom)
889FULLVERSION=$(VERSION)-$(TAG)
890endif # ifeq ($(DISTTYPE),release)
891
892DISTTYPEDIR ?= $(DISTTYPE)
893RELEASE=$(shell sed -ne 's/\#define NODE_VERSION_IS_RELEASE \([01]\)/\1/p' src/node_version.h)
894PLATFORM=$(shell uname | tr '[:upper:]' '[:lower:]')
895ifeq ($(findstring os/390,$PLATFORM),os/390)
896PLATFORM ?= os390
897endif
898NPMVERSION=v$(shell cat deps/npm/package.json | grep '^  "version"' | sed 's/^[^:]*: "\([^"]*\)",.*/\1/')
899
900UNAME_M=$(shell uname -m)
901ifeq ($(findstring x86_64,$(UNAME_M)),x86_64)
902DESTCPU ?= x64
903else
904ifeq ($(findstring amd64,$(UNAME_M)),amd64)
905DESTCPU ?= x64
906else
907ifeq ($(findstring ppc64,$(UNAME_M)),ppc64)
908DESTCPU ?= ppc64
909else
910ifeq ($(findstring ppc,$(UNAME_M)),ppc)
911DESTCPU ?= ppc
912else
913ifeq ($(findstring s390x,$(UNAME_M)),s390x)
914DESTCPU ?= s390x
915else
916ifeq ($(findstring s390,$(UNAME_M)),s390)
917DESTCPU ?= s390
918else
919ifeq ($(findstring OS/390,$(shell uname -s)),OS/390)
920DESTCPU ?= s390x
921else
922ifeq ($(findstring arm64,$(UNAME_M)),arm64)
923DESTCPU ?= arm64
924else
925ifeq ($(findstring arm,$(UNAME_M)),arm)
926DESTCPU ?= arm
927else
928ifeq ($(findstring aarch64,$(UNAME_M)),aarch64)
929DESTCPU ?= arm64
930else
931ifeq ($(findstring powerpc,$(shell uname -p)),powerpc)
932DESTCPU ?= ppc64
933else
934ifeq ($(findstring riscv64,$(UNAME_M)),riscv64)
935DESTCPU ?= riscv64
936else
937DESTCPU ?= x86
938endif
939endif
940endif
941endif
942endif
943endif
944endif
945endif
946endif
947endif
948endif
949endif
950ifeq ($(DESTCPU),x64)
951ARCH=x64
952else
953ifeq ($(DESTCPU),arm)
954ARCH=arm
955else
956ifeq ($(DESTCPU),arm64)
957ARCH=arm64
958else
959ifeq ($(DESTCPU),ppc64)
960ARCH=ppc64
961else
962ifeq ($(DESTCPU),ppc)
963ARCH=ppc
964else
965ifeq ($(DESTCPU),s390)
966ARCH=s390
967else
968ifeq ($(DESTCPU),s390x)
969ARCH=s390x
970else
971ifeq ($(DESTCPU),riscv64)
972ARCH=riscv64
973else
974ARCH=x86
975endif
976endif
977endif
978endif
979endif
980endif
981endif
982endif
983
984# node and v8 use different arch names (e.g. node 'x86' vs v8 'ia32').
985# pass the proper v8 arch name to $V8_ARCH based on user-specified $DESTCPU.
986ifeq ($(DESTCPU),x86)
987V8_ARCH=ia32
988else
989V8_ARCH ?= $(DESTCPU)
990
991endif
992
993# enforce "x86" over "ia32" as the generally accepted way of referring to 32-bit intel
994ifeq ($(ARCH),ia32)
995override ARCH=x86
996endif
997ifeq ($(DESTCPU),ia32)
998override DESTCPU=x86
999endif
1000
1001TARNAME=node-$(FULLVERSION)
1002TARBALL=$(TARNAME).tar
1003# Custom user-specified variation, use it directly
1004ifdef VARIATION
1005BINARYNAME=$(TARNAME)-$(PLATFORM)-$(ARCH)-$(VARIATION)
1006else
1007BINARYNAME=$(TARNAME)-$(PLATFORM)-$(ARCH)
1008endif
1009BINARYTAR=$(BINARYNAME).tar
1010# OSX doesn't have xz installed by default, http://macpkg.sourceforge.net/
1011HAS_XZ ?= $(shell command -v xz > /dev/null 2>&1; [ $$? -eq 0 ] && echo 1 || echo 0)
1012# Supply SKIP_XZ=1 to explicitly skip .tar.xz creation
1013SKIP_XZ ?= 0
1014XZ = $(shell [ $(HAS_XZ) -eq 1 ] && [ $(SKIP_XZ) -eq 0 ] && echo 1 || echo 0)
1015XZ_COMPRESSION ?= 9e
1016PKG=$(TARNAME).pkg
1017MACOSOUTDIR=out/macos
1018
1019.PHONY: check-xz
1020ifeq ($(SKIP_XZ), 1)
1021check-xz:
1022	$(info SKIP_XZ=1 supplied, skipping .tar.xz creation)
1023else
1024ifeq ($(HAS_XZ), 1)
1025check-xz:
1026else
1027check-xz:
1028	$(error No xz command, cannot continue)
1029endif
1030endif
1031
1032.PHONY: release-only
1033release-only: check-xz
1034	@if [ "$(DISTTYPE)" = "release" ] && `grep -q REPLACEME doc/api/*.md`; then \
1035		echo 'Please update REPLACEME tags in the following doc/api/*.md files (See doc/contributing/releases.md):\n' ; \
1036		REPLACEMES="$(shell grep -l REPLACEME doc/api/*.md)" ; \
1037		echo "$$REPLACEMES\n" | tr " " "\n" ; \
1038		exit 1 ; \
1039	fi
1040	@if [ "$(DISTTYPE)" = "release" ] && \
1041		`grep -q DEP...X doc/api/deprecations.md`; then \
1042		echo 'Please update DEP...X in doc/api/deprecations.md (See doc/contributing/releases.md)' ; \
1043		exit 1 ; \
1044	fi
1045	@if [ "$(shell git status --porcelain | egrep -v '^\?\? ')" = "" ]; then \
1046		exit 0 ; \
1047	else \
1048		echo "" >&2 ; \
1049		echo "The git repository is not clean." >&2 ; \
1050		echo "Please commit changes before building release tarball." >&2 ; \
1051		echo "" >&2 ; \
1052		git status --porcelain | egrep -v '^\?\?' >&2 ; \
1053		echo "" >&2 ; \
1054		exit 1 ; \
1055	fi
1056	@if [ "$(DISTTYPE)" != "release" ] || [ "$(RELEASE)" = "1" ]; then \
1057		exit 0; \
1058	else \
1059		echo "" >&2 ; \
1060		echo "#NODE_VERSION_IS_RELEASE is set to $(RELEASE)." >&2 ; \
1061		echo "Did you remember to update src/node_version.h?" >&2 ; \
1062		echo "" >&2 ; \
1063		exit 1 ; \
1064	fi
1065	@if [ "$(RELEASE)" = "0" ] || [ -f "$(CHANGELOG)" ]; then \
1066		exit 0; \
1067	else \
1068		echo "" >&2 ; \
1069		echo "#NODE_VERSION_IS_RELEASE is set to $(RELEASE) but " >&2 ; \
1070		echo "$(CHANGELOG) does not exist." >&2 ; \
1071		echo "" >&2 ; \
1072		exit 1 ; \
1073	fi
1074
1075$(PKG): release-only
1076# pkg building is currently only supported on an ARM64 macOS host for
1077# ease of compiling fat-binaries for both macOS architectures.
1078ifneq ($(OSTYPE),darwin)
1079	$(warning Invalid OSTYPE)
1080	$(error OSTYPE should be `darwin` currently is $(OSTYPE))
1081endif
1082ifneq ($(ARCHTYPE),arm64)
1083	$(warning Invalid ARCHTYPE)
1084	$(error ARCHTYPE should be `arm64` currently is $(ARCHTYPE))
1085endif
1086	$(RM) -r $(MACOSOUTDIR)
1087	mkdir -p $(MACOSOUTDIR)/installer/productbuild
1088	cat tools/macos-installer/productbuild/distribution.xml.tmpl  \
1089		| sed -E "s/\\{nodeversion\\}/$(FULLVERSION)/g" \
1090		| sed -E "s/\\{npmversion\\}/$(NPMVERSION)/g" \
1091	>$(MACOSOUTDIR)/installer/productbuild/distribution.xml ; \
1092
1093	@for dirname in tools/macos-installer/productbuild/Resources/*/; do \
1094		lang=$$(basename $$dirname) ; \
1095		mkdir -p $(MACOSOUTDIR)/installer/productbuild/Resources/$$lang ; \
1096		printf "Found localization directory $$dirname\n" ; \
1097		cat $$dirname/welcome.html.tmpl  \
1098			| sed -E "s/\\{nodeversion\\}/$(FULLVERSION)/g" \
1099			| sed -E "s/\\{npmversion\\}/$(NPMVERSION)/g"  \
1100		>$(MACOSOUTDIR)/installer/productbuild/Resources/$$lang/welcome.html ; \
1101		cat $$dirname/conclusion.html.tmpl  \
1102			| sed -E "s/\\{nodeversion\\}/$(FULLVERSION)/g" \
1103			| sed -E "s/\\{npmversion\\}/$(NPMVERSION)/g"  \
1104		>$(MACOSOUTDIR)/installer/productbuild/Resources/$$lang/conclusion.html ; \
1105	done
1106	CC_host="cc -arch x86_64" CXX_host="c++ -arch x86_64"  \
1107	CC_target="cc -arch x86_64" CXX_target="c++ -arch x86_64" \
1108	CC="cc -arch x86_64" CXX="c++ -arch x86_64" $(PYTHON) ./configure \
1109		--dest-cpu=x86_64 \
1110		--tag=$(TAG) \
1111		--release-urlbase=$(RELEASE_URLBASE) \
1112		$(CONFIG_FLAGS) $(BUILD_RELEASE_FLAGS)
1113	arch -x86_64 $(MAKE) install V=$(V) DESTDIR=$(MACOSOUTDIR)/dist/x64/node
1114	SIGN="$(CODESIGN_CERT)" PKGDIR="$(MACOSOUTDIR)/dist/x64/node/usr/local" sh \
1115		tools/osx-codesign.sh
1116	$(PYTHON) ./configure \
1117		--dest-cpu=arm64 \
1118		--tag=$(TAG) \
1119		--release-urlbase=$(RELEASE_URLBASE) \
1120		$(CONFIG_FLAGS) $(BUILD_RELEASE_FLAGS)
1121	$(MAKE) install V=$(V) DESTDIR=$(MACOSOUTDIR)/dist/node
1122	SIGN="$(CODESIGN_CERT)" PKGDIR="$(MACOSOUTDIR)/dist/node/usr/local" sh \
1123		tools/osx-codesign.sh
1124	lipo $(MACOSOUTDIR)/dist/x64/node/usr/local/bin/node \
1125		$(MACOSOUTDIR)/dist/node/usr/local/bin/node \
1126		-output $(MACOSOUTDIR)/dist/node/usr/local/bin/node \
1127		-create
1128	mkdir -p $(MACOSOUTDIR)/dist/npm/usr/local/lib/node_modules
1129	mkdir -p $(MACOSOUTDIR)/pkgs
1130	mv $(MACOSOUTDIR)/dist/node/usr/local/lib/node_modules/npm \
1131		$(MACOSOUTDIR)/dist/npm/usr/local/lib/node_modules
1132	unlink $(MACOSOUTDIR)/dist/node/usr/local/bin/npm
1133	unlink $(MACOSOUTDIR)/dist/node/usr/local/bin/npx
1134	$(NODE) tools/license2rtf.mjs < LICENSE > \
1135		$(MACOSOUTDIR)/installer/productbuild/Resources/license.rtf
1136	cp doc/osx_installer_logo.png $(MACOSOUTDIR)/installer/productbuild/Resources
1137	pkgbuild --version $(FULLVERSION) \
1138		--identifier org.nodejs.node.pkg \
1139		--root $(MACOSOUTDIR)/dist/node $(MACOSOUTDIR)/pkgs/node-$(FULLVERSION).pkg
1140	pkgbuild --version $(NPMVERSION) \
1141		--identifier org.nodejs.npm.pkg \
1142		--root $(MACOSOUTDIR)/dist/npm \
1143		--scripts ./tools/macos-installer/pkgbuild/npm/scripts \
1144			$(MACOSOUTDIR)/pkgs/npm-$(NPMVERSION).pkg
1145	productbuild --distribution $(MACOSOUTDIR)/installer/productbuild/distribution.xml \
1146		--resources $(MACOSOUTDIR)/installer/productbuild/Resources \
1147		--package-path $(MACOSOUTDIR)/pkgs ./$(PKG)
1148	SIGN="$(PRODUCTSIGN_CERT)" PKG="$(PKG)" sh tools/osx-productsign.sh
1149	sh tools/osx-notarize.sh $(FULLVERSION)
1150
1151.PHONY: pkg
1152# Builds the macOS installer for releases.
1153pkg: $(PKG)
1154
1155.PHONY: corepack-update
1156corepack-update:
1157	mkdir -p /tmp/node-corepack
1158	curl -qLo /tmp/node-corepack/package.tgz "$$(npm view corepack dist.tarball)"
1159
1160	rm -rf deps/corepack && mkdir deps/corepack
1161	cd deps/corepack && tar xf /tmp/node-corepack/package.tgz --strip-components=1
1162	chmod +x deps/corepack/shims/*
1163
1164	node deps/corepack/dist/corepack.js --version
1165
1166.PHONY: pkg-upload
1167# Note: this is strictly for release builds on release machines only.
1168pkg-upload: pkg
1169	ssh $(STAGINGSERVER) "mkdir -p nodejs/$(DISTTYPEDIR)/$(FULLVERSION)"
1170	chmod 664 $(TARNAME).pkg
1171	scp -p $(TARNAME).pkg $(STAGINGSERVER):nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME).pkg
1172	ssh $(STAGINGSERVER) "touch nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME).pkg.done"
1173
1174$(TARBALL): release-only doc-only
1175	git checkout-index -a -f --prefix=$(TARNAME)/
1176	mkdir -p $(TARNAME)/doc/api
1177	cp doc/node.1 $(TARNAME)/doc/node.1
1178	cp -r out/doc/api/* $(TARNAME)/doc/api/
1179	$(RM) -r $(TARNAME)/.editorconfig
1180	$(RM) -r $(TARNAME)/.git*
1181	$(RM) -r $(TARNAME)/.mailmap
1182	$(RM) -r $(TARNAME)/deps/openssl/openssl/demos
1183	$(RM) -r $(TARNAME)/deps/openssl/openssl/doc
1184	$(RM) -r $(TARNAME)/deps/openssl/openssl/test
1185	$(RM) -r $(TARNAME)/deps/uv/docs
1186	$(RM) -r $(TARNAME)/deps/uv/samples
1187	$(RM) -r $(TARNAME)/deps/uv/test
1188	$(RM) -r $(TARNAME)/deps/v8/samples
1189	$(RM) -r $(TARNAME)/deps/v8/tools/profviz
1190	$(RM) -r $(TARNAME)/deps/v8/tools/run-tests.py
1191	$(RM) -r $(TARNAME)/doc/images # too big
1192	$(RM) -r $(TARNAME)/test*.tap
1193	$(RM) -r $(TARNAME)/tools/cpplint.py
1194	$(RM) -r $(TARNAME)/tools/eslint-rules
1195	$(RM) -r $(TARNAME)/tools/license-builder.sh
1196	$(RM) -r $(TARNAME)/tools/node_modules
1197	$(RM) -r $(TARNAME)/tools/osx-*
1198	$(RM) -r $(TARNAME)/tools/osx-pkg.pmdoc
1199	find $(TARNAME)/deps/v8/test/* -type d ! -regex '.*/test/torque$$' | xargs $(RM) -r
1200	find $(TARNAME)/deps/v8/test -type f ! -regex '.*/test/torque/.*' | xargs $(RM)
1201	find $(TARNAME)/deps/zlib/contrib/* -type d ! -regex '.*/contrib/optimizations$$' | xargs $(RM) -r
1202	find $(TARNAME)/ -name ".eslint*" -maxdepth 2 | xargs $(RM)
1203	find $(TARNAME)/ -type l | xargs $(RM)
1204	tar -cf $(TARNAME).tar $(TARNAME)
1205	$(RM) -r $(TARNAME)
1206	gzip -c -f -9 $(TARNAME).tar > $(TARNAME).tar.gz
1207ifeq ($(XZ), 1)
1208	xz -c -f -$(XZ_COMPRESSION) $(TARNAME).tar > $(TARNAME).tar.xz
1209endif
1210	$(RM) $(TARNAME).tar
1211
1212.PHONY: tar
1213tar: $(TARBALL) ## Create a source tarball.
1214
1215.PHONY: tar-upload
1216# Note: this is strictly for release builds on release machines only.
1217tar-upload: tar
1218	ssh $(STAGINGSERVER) "mkdir -p nodejs/$(DISTTYPEDIR)/$(FULLVERSION)"
1219	chmod 664 $(TARNAME).tar.gz
1220	scp -p $(TARNAME).tar.gz $(STAGINGSERVER):nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME).tar.gz
1221	ssh $(STAGINGSERVER) "touch nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME).tar.gz.done"
1222ifeq ($(XZ), 1)
1223	chmod 664 $(TARNAME).tar.xz
1224	scp -p $(TARNAME).tar.xz $(STAGINGSERVER):nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME).tar.xz
1225	ssh $(STAGINGSERVER) "touch nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME).tar.xz.done"
1226endif
1227
1228.PHONY: doc-upload
1229# Note: this is strictly for release builds on release machines only.
1230doc-upload: doc
1231	ssh $(STAGINGSERVER) "mkdir -p nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/docs/"
1232	chmod -R ug=rw-x+X,o=r+X out/doc/
1233	scp -pr out/doc/* $(STAGINGSERVER):nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/docs/
1234	ssh $(STAGINGSERVER) "touch nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/docs.done"
1235
1236.PHONY: $(TARBALL)-headers
1237$(TARBALL)-headers: release-only
1238	$(PYTHON) ./configure \
1239		--prefix=/ \
1240		--dest-cpu=$(DESTCPU) \
1241		--tag=$(TAG) \
1242		--release-urlbase=$(RELEASE_URLBASE) \
1243		$(CONFIG_FLAGS) $(BUILD_RELEASE_FLAGS)
1244	HEADERS_ONLY=1 $(PYTHON) tools/install.py install '$(TARNAME)' '/'
1245	find $(TARNAME)/ -type l | xargs $(RM)
1246	tar -cf $(TARNAME)-headers.tar $(TARNAME)
1247	$(RM) -r $(TARNAME)
1248	gzip -c -f -9 $(TARNAME)-headers.tar > $(TARNAME)-headers.tar.gz
1249ifeq ($(XZ), 1)
1250	xz -c -f -$(XZ_COMPRESSION) $(TARNAME)-headers.tar > $(TARNAME)-headers.tar.xz
1251endif
1252	$(RM) $(TARNAME)-headers.tar
1253
1254.PHONY: tar-headers
1255tar-headers: $(TARBALL)-headers ## Build the node header tarball.
1256
1257.PHONY: tar-headers-upload
1258tar-headers-upload: tar-headers
1259	ssh $(STAGINGSERVER) "mkdir -p nodejs/$(DISTTYPEDIR)/$(FULLVERSION)"
1260	chmod 664 $(TARNAME)-headers.tar.gz
1261	scp -p $(TARNAME)-headers.tar.gz $(STAGINGSERVER):nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME)-headers.tar.gz
1262	ssh $(STAGINGSERVER) "touch nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME)-headers.tar.gz.done"
1263ifeq ($(XZ), 1)
1264	chmod 664 $(TARNAME)-headers.tar.xz
1265	scp -p $(TARNAME)-headers.tar.xz $(STAGINGSERVER):nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME)-headers.tar.xz
1266	ssh $(STAGINGSERVER) "touch nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME)-headers.tar.xz.done"
1267endif
1268
1269$(BINARYTAR): release-only
1270	$(RM) -r $(BINARYNAME)
1271	$(RM) -r out/deps out/Release
1272	$(PYTHON) ./configure \
1273		--prefix=/ \
1274		--dest-cpu=$(DESTCPU) \
1275		--tag=$(TAG) \
1276		--release-urlbase=$(RELEASE_URLBASE) \
1277		$(CONFIG_FLAGS) $(BUILD_RELEASE_FLAGS)
1278	$(MAKE) install DESTDIR=$(BINARYNAME) V=$(V) PORTABLE=1
1279	cp README.md $(BINARYNAME)
1280	cp LICENSE $(BINARYNAME)
1281ifeq ("$(wildcard $(CHANGELOG))","")
1282	cp CHANGELOG.md $(BINARYNAME)
1283else
1284	cp $(CHANGELOG) $(BINARYNAME)/CHANGELOG.md
1285endif
1286ifeq ($(OSTYPE),darwin)
1287	SIGN="$(CODESIGN_CERT)" PKGDIR="$(BINARYNAME)" sh tools/osx-codesign.sh
1288endif
1289	tar -cf $(BINARYNAME).tar $(BINARYNAME)
1290	$(RM) -r $(BINARYNAME)
1291	gzip -c -f -9 $(BINARYNAME).tar > $(BINARYNAME).tar.gz
1292ifeq ($(XZ), 1)
1293	xz -c -f -$(XZ_COMPRESSION) $(BINARYNAME).tar > $(BINARYNAME).tar.xz
1294endif
1295	$(RM) $(BINARYNAME).tar
1296
1297.PHONY: binary
1298# This requires NODE_VERSION_IS_RELEASE defined as 1 in src/node_version.h.
1299binary: $(BINARYTAR) ## Build release binary tarballs.
1300
1301.PHONY: binary-upload
1302# Note: this is strictly for release builds on release machines only.
1303binary-upload: binary
1304	ssh $(STAGINGSERVER) "mkdir -p nodejs/$(DISTTYPEDIR)/$(FULLVERSION)"
1305	chmod 664 $(TARNAME)-$(OSTYPE)-$(ARCH).tar.gz
1306	scp -p $(TARNAME)-$(OSTYPE)-$(ARCH).tar.gz $(STAGINGSERVER):nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME)-$(OSTYPE)-$(ARCH).tar.gz
1307	ssh $(STAGINGSERVER) "touch nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME)-$(OSTYPE)-$(ARCH).tar.gz.done"
1308ifeq ($(XZ), 1)
1309	chmod 664 $(TARNAME)-$(OSTYPE)-$(ARCH).tar.xz
1310	scp -p $(TARNAME)-$(OSTYPE)-$(ARCH).tar.xz $(STAGINGSERVER):nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME)-$(OSTYPE)-$(ARCH).tar.xz
1311	ssh $(STAGINGSERVER) "touch nodejs/$(DISTTYPEDIR)/$(FULLVERSION)/$(TARNAME)-$(OSTYPE)-$(ARCH).tar.xz.done"
1312endif
1313
1314.PHONY: bench-all
1315.PHONY: bench
1316bench bench-all: bench-addons-build
1317	$(warning Please use benchmark/run.js or benchmark/compare.js to run the benchmarks.)
1318
1319# Build required addons for benchmark before running it.
1320.PHONY: bench-addons-build
1321bench-addons-build: | $(NODE_EXE) benchmark/napi/.buildstamp
1322
1323.PHONY: bench-addons-clean
1324.NOTPARALLEL: bench-addons-clean
1325bench-addons-clean:
1326	$(RM) -r benchmark/napi/*/build
1327	$(RM) benchmark/napi/.buildstamp
1328
1329.PHONY: lint-md-rollup
1330lint-md-rollup:
1331	$(RM) tools/.*mdlintstamp
1332	cd tools/lint-md && npm ci && npm run build
1333
1334.PHONY: lint-md-clean
1335.NOTPARALLEL: lint-md-clean
1336lint-md-clean:
1337	$(RM) -r tools/lint-md/node_modules
1338	$(RM) tools/.*mdlintstamp
1339
1340.PHONY: lint-md-build
1341lint-md-build:
1342	$(warning Deprecated no-op target 'lint-md-build')
1343
1344ifeq ("$(wildcard tools/.mdlintstamp)","")
1345LINT_MD_NEWER =
1346else
1347LINT_MD_NEWER = -newer tools/.mdlintstamp
1348endif
1349
1350LINT_MD_TARGETS = doc src lib benchmark test tools/doc tools/icu $(wildcard *.md)
1351LINT_MD_FILES = $(shell $(FIND) $(LINT_MD_TARGETS) -type f \
1352	! -path '*node_modules*' ! -path 'test/fixtures/*' -name '*.md' \
1353	$(LINT_MD_NEWER))
1354run-lint-md = tools/lint-md/lint-md.mjs $(LINT_MD_FILES)
1355# Lint all changed markdown files maintained by us
1356tools/.mdlintstamp: $(LINT_MD_FILES)
1357	$(info Running Markdown linter...)
1358	@$(call available-node,$(run-lint-md))
1359	@touch $@
1360
1361.PHONY: lint-md
1362# Lints the markdown documents maintained by us in the codebase.
1363lint-md: lint-js-doc | tools/.mdlintstamp
1364
1365run-format-md = tools/lint-md/lint-md.mjs --format $(LINT_MD_FILES)
1366.PHONY: format-md
1367# Formats the markdown documents maintained by us in the codebase.
1368format-md:
1369	@$(call available-node,$(run-format-md))
1370
1371
1372
1373LINT_JS_TARGETS = .eslintrc.js benchmark doc lib test tools
1374
1375run-lint-js = tools/node_modules/eslint/bin/eslint.js --cache \
1376	--max-warnings=0 --report-unused-disable-directives $(LINT_JS_TARGETS)
1377run-lint-js-fix = $(run-lint-js) --fix
1378
1379.PHONY: lint-js-fix
1380lint-js-fix:
1381	@$(call available-node,$(run-lint-js-fix))
1382
1383.PHONY: lint-js
1384.PHONY: lint-js-doc
1385# Note that on the CI `lint-js-ci` is run instead.
1386# Lints the JavaScript code with eslint.
1387lint-js-doc: LINT_JS_TARGETS=doc
1388lint-js lint-js-doc:
1389	@if [ "$(shell $(node_use_openssl))" != "true" ]; then \
1390		echo "Skipping $@ (no crypto)"; \
1391	else \
1392		echo "Running JS linter..."; \
1393		$(call available-node,$(run-lint-js)) \
1394	fi
1395
1396jslint: lint-js
1397	$(warning Please use lint-js instead of jslint)
1398
1399run-lint-js-ci = tools/node_modules/eslint/bin/eslint.js \
1400  --max-warnings=0 --report-unused-disable-directives -f tap \
1401	-o test-eslint.tap $(LINT_JS_TARGETS)
1402
1403.PHONY: lint-js-ci
1404# On the CI the output is emitted in the TAP format.
1405lint-js-ci:
1406	$(info Running JS linter...)
1407	@$(call available-node,$(run-lint-js-ci))
1408
1409jslint-ci: lint-js-ci
1410	$(warning Please use lint-js-ci instead of jslint-ci)
1411
1412LINT_CPP_ADDON_DOC_FILES_GLOB = test/addons/??_*/*.cc test/addons/??_*/*.h
1413LINT_CPP_ADDON_DOC_FILES = $(wildcard $(LINT_CPP_ADDON_DOC_FILES_GLOB))
1414LINT_CPP_EXCLUDE ?=
1415LINT_CPP_EXCLUDE += src/node_root_certs.h
1416LINT_CPP_EXCLUDE += $(LINT_CPP_ADDON_DOC_FILES)
1417# These files were copied more or less verbatim from V8.
1418LINT_CPP_EXCLUDE += src/tracing/trace_event.h src/tracing/trace_event_common.h
1419
1420LINT_CPP_FILES = $(filter-out $(LINT_CPP_EXCLUDE), $(wildcard \
1421	benchmark/napi/*/*.cc \
1422	src/*.c \
1423	src/*.cc \
1424	src/*.h \
1425	src/*/*.c \
1426	src/*/*.cc \
1427	src/*/*.h \
1428	test/addons/*/*.cc \
1429	test/addons/*/*.h \
1430	test/cctest/*.cc \
1431	test/cctest/*.h \
1432	test/embedding/*.cc \
1433	test/embedding/*.h \
1434	test/fixtures/*.c \
1435	test/js-native-api/*/*.cc \
1436	test/node-api/*/*.cc \
1437	tools/icu/*.cc \
1438	tools/icu/*.h \
1439	tools/code_cache/*.cc \
1440	tools/code_cache/*.h \
1441	tools/snapshot/*.cc \
1442	tools/snapshot/*.h \
1443	))
1444
1445FORMAT_CPP_FILES ?=
1446FORMAT_CPP_FILES += $(LINT_CPP_FILES)
1447# C source codes.
1448FORMAT_CPP_FILES += $(wildcard \
1449	benchmark/napi/*/*.c \
1450	test/js-native-api/*.h \
1451	test/js-native-api/*/*.c \
1452	test/js-native-api/*/*.h \
1453	test/node-api/*/*.c \
1454	test/node-api/*/*.h \
1455	)
1456
1457# Code blocks don't have newline at the end,
1458# and the actual filename is generated so it won't match header guards
1459ADDON_DOC_LINT_FLAGS=-whitespace/ending_newline,-build/header_guard
1460
1461.PHONY: format-cpp-build
1462format-cpp-build:
1463	cd tools/clang-format && $(call available-node,$(run-npm-ci))
1464
1465.PHONY: format-cpp-clean
1466.NOTPARALLEL: format-cpp-clean
1467format-cpp-clean:
1468	$(RM) -r tools/clang-format/node_modules
1469
1470CLANG_FORMAT_START ?= HEAD
1471.PHONY: format-cpp
1472# To format staged changes:
1473#  $ make format-cpp
1474# To format HEAD~1...HEAD (latest commit):
1475#  $ CLANG_FORMAT_START=`git rev-parse HEAD~1` make format-cpp
1476# To format diff between main and current branch head (main...HEAD):
1477#  $ CLANG_FORMAT_START=main make format-cpp
1478format-cpp: ## Format C++ diff from $CLANG_FORMAT_START to current changes
1479ifneq ("","$(wildcard tools/clang-format/node_modules/)")
1480	$(info Formatting C++ diff from $(CLANG_FORMAT_START)..)
1481	@$(PYTHON) tools/clang-format/node_modules/.bin/git-clang-format \
1482		--binary=tools/clang-format/node_modules/.bin/clang-format \
1483		--style=file \
1484		$(CLANG_FORMAT_START) -- \
1485		$(FORMAT_CPP_FILES)
1486else
1487	$(info Required tooling for C++ code formatting is not installed.)
1488	$(info To install (requires internet access) run: $$ make format-cpp-build)
1489endif
1490
1491ifeq ($(V),1)
1492CPPLINT_QUIET =
1493else
1494CPPLINT_QUIET = --quiet
1495endif
1496.PHONY: lint-cpp
1497# Lints the C++ code with cpplint.py and checkimports.py.
1498lint-cpp: tools/.cpplintstamp
1499
1500tools/.cpplintstamp: $(LINT_CPP_FILES)
1501	$(info Running C++ linter...)
1502	@$(PYTHON) tools/cpplint.py $(CPPLINT_QUIET) $?
1503	@$(PYTHON) tools/checkimports.py $?
1504	@touch $@
1505
1506.PHONY: lint-addon-docs
1507lint-addon-docs: tools/.doclintstamp
1508
1509tools/.doclintstamp: test/addons/.docbuildstamp
1510	$(info Running C++ linter on addon docs...)
1511	@$(PYTHON) tools/cpplint.py $(CPPLINT_QUIET) --filter=$(ADDON_DOC_LINT_FLAGS) \
1512		$(LINT_CPP_ADDON_DOC_FILES_GLOB)
1513	@touch $@
1514
1515cpplint: lint-cpp
1516	$(warning Please use lint-cpp instead of cpplint)
1517
1518.PHONY: lint-py-build
1519# python -m pip install ruff
1520# Try with '--system' if it fails without; the system may have set '--user'
1521lint-py-build:
1522	$(info Pip installing ruff on $(shell $(PYTHON) --version)...)
1523	$(PYTHON) -m pip install --upgrade --target tools/pip/site-packages ruff==0.0.272 || \
1524		$(PYTHON) -m pip install --upgrade --system --target tools/pip/site-packages ruff==0.0.272
1525
1526.PHONY: lint-py
1527ifneq ("","$(wildcard tools/pip/site-packages/ruff)")
1528# Lint the Python code with ruff.
1529lint-py:
1530	tools/pip/site-packages/bin/ruff --version
1531	tools/pip/site-packages/bin/ruff .
1532else
1533lint-py:
1534	$(warning Python linting with ruff is not available)
1535	$(warning Run 'make lint-py-build')
1536endif
1537
1538.PHONY: lint-yaml-build
1539# python -m pip install yamllint
1540# Try with '--system' if it fails without; the system may have set '--user'
1541lint-yaml-build:
1542	$(info Pip installing yamllint on $(shell $(PYTHON) --version)...)
1543	$(PYTHON) -m pip install --upgrade -t tools/pip/site-packages yamllint || \
1544		$(PYTHON) -m pip install --upgrade --system -t tools/pip/site-packages yamllint
1545
1546.PHONY: lint-yaml
1547# Lints the YAML files with yamllint.
1548lint-yaml:
1549	@if [ -d "tools/pip/site-packages/yamllint" ]; then \
1550			PYTHONPATH=tools/pip $(PYTHON) -m yamllint .; \
1551	else \
1552		echo 'YAML linting with yamllint is not available'; \
1553		echo "Run 'make lint-yaml-build'"; \
1554	fi
1555
1556.PHONY: lint
1557.PHONY: lint-ci
1558ifneq ("","$(wildcard tools/node_modules/eslint/)")
1559lint: ## Run JS, C++, MD and doc linters.
1560	@EXIT_STATUS=0 ; \
1561	$(MAKE) lint-js || EXIT_STATUS=$$? ; \
1562	$(MAKE) lint-cpp || EXIT_STATUS=$$? ; \
1563	$(MAKE) lint-addon-docs || EXIT_STATUS=$$? ; \
1564	$(MAKE) lint-md || EXIT_STATUS=$$? ; \
1565	$(MAKE) lint-yaml || EXIT_STATUS=$$? ; \
1566	exit $$EXIT_STATUS
1567CONFLICT_RE=^>>>>>>> [[:xdigit:]]+|^<<<<<<< [[:alpha:]]+
1568
1569# Related CI job: node-test-linter
1570lint-ci: lint-js-ci lint-cpp lint-py lint-md lint-addon-docs lint-yaml-build lint-yaml
1571	@if ! ( grep -IEqrs "$(CONFLICT_RE)" --exclude="error-message.js" --exclude="merge-conflict.json" benchmark deps doc lib src test tools ) \
1572		&& ! ( $(FIND) . -maxdepth 1 -type f | xargs grep -IEqs "$(CONFLICT_RE)" ); then \
1573		exit 0 ; \
1574	else \
1575		echo "" >&2 ; \
1576		echo "Conflict marker detected in one or more files. Please fix them first." >&2 ; \
1577		exit 1 ; \
1578	fi
1579else
1580lint lint-ci:
1581	$(info Linting is not available through the source tarball.)
1582	$(info Use the git repo instead: $$ git clone https://github.com/nodejs/node.git)
1583endif
1584
1585.PHONY: lint-clean
1586lint-clean:
1587	$(RM) tools/.*lintstamp
1588	$(RM) .eslintcache
1589
1590HAS_DOCKER ?= $(shell command -v docker > /dev/null 2>&1; [ $$? -eq 0 ] && echo 1 || echo 0)
1591
1592.PHONY: gen-openssl
1593ifeq ($(HAS_DOCKER), 1)
1594DOCKER_COMMAND ?= docker run -it -v $(PWD):/node
1595IS_IN_WORKTREE = $(shell grep '^gitdir: ' $(PWD)/.git 2>/dev/null)
1596GIT_WORKTREE_COMMON = $(shell git rev-parse --git-common-dir)
1597DOCKER_COMMAND += $(if $(IS_IN_WORKTREE), -v $(GIT_WORKTREE_COMMON):$(GIT_WORKTREE_COMMON))
1598gen-openssl: ## Generate platform dependent openssl files (requires docker)
1599	docker build -t node-openssl-builder deps/openssl/config/
1600	$(DOCKER_COMMAND) node-openssl-builder make -C deps/openssl/config
1601else
1602gen-openssl:
1603	$(error No docker command, cannot continue)
1604endif
1605