@brnshkr/config 0.0.1-beta.2 → 0.0.1-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +177 -228
- package/conf/.gitignore.dist +15 -0
- package/conf/Makefile +3778 -0
- package/conf/Makefile.dist +13 -0
- package/conf/bunfig.dist.toml +5 -0
- package/conf/commitlint.dist.mjs +1 -0
- package/conf/editorconfig.dist +19 -0
- package/conf/launch.dist.json +31 -0
- package/conf/markdownlint.dist.mjs +1 -0
- package/conf/spelling/defaults.json +185 -0
- package/conf/tsconfig.dist.json +3 -0
- package/conf/tsconfig.json +31 -27
- package/conf/vitest.dist.mjs +1 -0
- package/conf/vscode-css-custom-data.dist.json +95 -0
- package/conf/vscode-extensions.dist.json +21 -0
- package/conf/vscode-settings.dist.jsonc +338 -0
- package/dist/commitlint/index.d.mts +72 -0
- package/dist/commitlint/index.mjs +239 -0
- package/dist/eslint/index.d.mts +3897 -1383
- package/dist/eslint/index.mjs +2784 -419
- package/dist/markdownlint/index.d.mts +2304 -0
- package/dist/markdownlint/index.mjs +440 -0
- package/dist/shared.mjs +405 -65
- package/dist/spelling/index.d.mts +30 -0
- package/dist/spelling/index.mjs +2 -0
- package/dist/spelling/spelling.test.d.mts +1 -0
- package/dist/spelling/spelling.test.mjs +12 -0
- package/dist/stylelint/index.d.mts +47 -9
- package/dist/stylelint/index.mjs +145 -47
- package/dist/vitest/index.d.mts +73 -0
- package/dist/vitest/index.mjs +181 -0
- package/package.json +192 -105
- package/conf/tsconfig.json.example +0 -3
- package/dist/scripts/eslint.mjs +0 -16
- package/dist/scripts/stylelint.mjs +0 -29
- /package/conf/{eslint.config.mjs.example → eslint.dist.mjs} +0 -0
- /package/conf/{stylelint.config.mjs.example → stylelint.dist.mjs} +0 -0
package/conf/Makefile
ADDED
|
@@ -0,0 +1,3778 @@
|
|
|
1
|
+
#==
|
|
2
|
+
# brnshkr/config Makefile
|
|
3
|
+
# ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
|
|
4
|
+
# This Makefile provides shared tooling, conventions, and utilities that can be
|
|
5
|
+
# included and extended by project-specific Makefiles. It implements a small
|
|
6
|
+
# framework around GNU Make to provide:
|
|
7
|
+
# - structured CLI-style commands
|
|
8
|
+
# - argument forwarding to targets
|
|
9
|
+
# - automatic help generation
|
|
10
|
+
# - terminal formatting helpers
|
|
11
|
+
#
|
|
12
|
+
# Variables and macros prefixed with an underscore ('_') are considered private
|
|
13
|
+
# implementation details and are not part of the public API.
|
|
14
|
+
#
|
|
15
|
+
# Macros that are written in UPPER_SNAKE_CASE are treated as variables.
|
|
16
|
+
# Macros that are written in lower_snake_case are treated as functions.
|
|
17
|
+
#
|
|
18
|
+
# This Makefile implements a custom argument parsing system that allows
|
|
19
|
+
# arguments to be passed to specific targets while preserving Make's standard behavior:
|
|
20
|
+
#
|
|
21
|
+
# Rules:
|
|
22
|
+
# - Arguments that do NOT match an existing target are treated as arguments
|
|
23
|
+
# - Arguments before the first valid target are passed to ALL invoked targets
|
|
24
|
+
# - Arguments between two targets are passed only to the preceding target
|
|
25
|
+
# - Runs that contain no valid targets trigger the error defined in .DEFAULT target
|
|
26
|
+
# - Arguments shaped 'name=value' are taken by make itself, so an option's value is written with a space
|
|
27
|
+
# - Make claims a dash-prefixed word as its own option, so flags follow a '--' separator
|
|
28
|
+
#
|
|
29
|
+
# Examples of what the above-mentioned looks like:
|
|
30
|
+
# make a -> runs target a
|
|
31
|
+
# make a b -> runs targets a and b
|
|
32
|
+
# make a a1 a2 b b1 b2 -> runs target a with arguments a1 and a2 and target b with arguments b1 and b2 (assuming a1, a2, b1 and b2 are not targets by themselves)
|
|
33
|
+
# make ab1 ab2 a a1 b -> runs target a with arguments ab1, ab2 and a1 and target b with arguments ab1 and ab2 (assuming ab1, ab2 and a1 are not targets by themselves)
|
|
34
|
+
# make no arg exists -> throws error defined in .DEFAULT target
|
|
35
|
+
#
|
|
36
|
+
# Any target can be prepended with the DEBUG flag to echo the commands as they run,
|
|
37
|
+
# or the TRACE flag to echo the guards along with them:
|
|
38
|
+
# - make DEBUG=1 phpstan
|
|
39
|
+
# - DEBUG=1 make phpstan
|
|
40
|
+
#
|
|
41
|
+
# A flag is off when it is empty or one of '0', 'false', 'off' and 'no', and on for any other value.
|
|
42
|
+
#
|
|
43
|
+
# All make runs can be prepend with the NO_ANSI flag to disable any ANSI escape sequences in the output:
|
|
44
|
+
# - make NO_ANSI=1 help
|
|
45
|
+
# - NO_ANSI=1 make help
|
|
46
|
+
#
|
|
47
|
+
# The `make help` command automatically generates documentation based on specially formatted comments:
|
|
48
|
+
# Scope headers (control grouping and nesting in the help output):
|
|
49
|
+
# #-- name — top-level group (depth 0); all subsequent symbols belong to this group
|
|
50
|
+
# #--- name — section (depth 1, nested inside the nearest depth-0 group)
|
|
51
|
+
# #---- name — subsection (depth 2)
|
|
52
|
+
# #-----… name — subsection (depth N)
|
|
53
|
+
#
|
|
54
|
+
# Append 'v' characters to set a minimum verbosity level for everything inside the scope.
|
|
55
|
+
# Arbitrary depth is supported — each additional 'v' raises the threshold by one:
|
|
56
|
+
# #---v name — contents hidden unless verbosity >= 1
|
|
57
|
+
# #---vv name — contents hidden unless verbosity >= 2
|
|
58
|
+
# #---vvv name — contents hidden unless verbosity >= 3
|
|
59
|
+
# (and so on)
|
|
60
|
+
#
|
|
61
|
+
# Inline description comments (attach metadata to a specific symbol):
|
|
62
|
+
# VARIABLE ?= value#~~ description — annotate a variable with a description
|
|
63
|
+
# command: #~~ description — annotate a command with a description
|
|
64
|
+
#
|
|
65
|
+
# Append 'v' characters after '#' to make the line itself require higher verbosity (also arbitrary depth):
|
|
66
|
+
# command: #~~ description #v — hide the command entry unless verbosity >= 1
|
|
67
|
+
# VARIABLE ?= value#vv #~~ … — hide the variable entry unless verbosity >= 2
|
|
68
|
+
# VARIABLE ?= value#~~ … #vvv — hide the variable entry unless verbosity >= 3
|
|
69
|
+
#
|
|
70
|
+
# Description block (shown at the top of the help output under "Description:"):
|
|
71
|
+
# #!! text — emit a free-form description line; multiple lines are supported
|
|
72
|
+
#
|
|
73
|
+
# Documentation blocks (shown for function macros at higher verbosities):
|
|
74
|
+
# #** — open a documentation block
|
|
75
|
+
# #* text — body line inside the block (leading '#* ' is stripped)
|
|
76
|
+
# The first non-comment, non-empty line after the block must be a 'define' statement.
|
|
77
|
+
#
|
|
78
|
+
# Filtering by scope(s) is possible:
|
|
79
|
+
# make help brnshkr
|
|
80
|
+
# make help brnshkr.phpstan
|
|
81
|
+
# make help brnshkr.phpstan brnshkr.rector
|
|
82
|
+
# make help brnshkr app.foo -- -v
|
|
83
|
+
#
|
|
84
|
+
# Selecting verbosity at the command line (arbitrary depth):
|
|
85
|
+
# make help v — verbosity 1
|
|
86
|
+
# make help vvv — verbosity 3
|
|
87
|
+
# make help vvvv — verbosity 4
|
|
88
|
+
# make help -- -vv — verbosity 2 (alias form)
|
|
89
|
+
# make help V=10 — verbosity 10 (numeric form via env var)
|
|
90
|
+
#
|
|
91
|
+
# Listing the scope dot-paths with something to show at the chosen verbosity:
|
|
92
|
+
# make help list-scopes
|
|
93
|
+
# make help ls
|
|
94
|
+
# make help ls vvv
|
|
95
|
+
# make help ls brnshkr
|
|
96
|
+
# make help ls brnshkr.theming app
|
|
97
|
+
#
|
|
98
|
+
# Printing what each variable expands to instead of its source text:
|
|
99
|
+
# make help resolve
|
|
100
|
+
# make help r
|
|
101
|
+
#
|
|
102
|
+
# Header overrides (LOGO, PACKAGE, VERSION, EDITOR):
|
|
103
|
+
# <VAR> = <unset> — fall back to the default (built-in logo, auto-detected editor)
|
|
104
|
+
# <VAR> = <value> — use the given value (EDITOR rejects values outside the supported list)
|
|
105
|
+
# <VAR> = — disable the feature (no logo, no clickable hyperlinks)
|
|
106
|
+
#
|
|
107
|
+
# EDITOR is the standard shell variable, so one inherited from the environment naming anything else
|
|
108
|
+
# is ignored rather than rejected; only a value set in a Makefile or on the command line is an error.
|
|
109
|
+
#==
|
|
110
|
+
|
|
111
|
+
#-- brnshkr
|
|
112
|
+
|
|
113
|
+
#!! Shared targets from the `brnshkr/config` package
|
|
114
|
+
|
|
115
|
+
_BRNSHKR_CONFIG_MAKEFILE := $(lastword $(MAKEFILE_LIST))
|
|
116
|
+
_BRNSHKR_CONFIG_DIR := $(patsubst %/,%,$(dir $(_BRNSHKR_CONFIG_MAKEFILE)))
|
|
117
|
+
|
|
118
|
+
_ARG_WORDS = $(patsubst -%,%,$(patsubst --%,%,$(_ARGS)))
|
|
119
|
+
|
|
120
|
+
_HELP_VERBOSITY_ARGS = $(strip $(foreach ARG_WORD,$(_ARG_WORDS),$(if $(subst v,,$(ARG_WORD)),,$(ARG_WORD))))
|
|
121
|
+
_HELP_VERBOSITY = $(words $(subst v,v ,$(lastword $(sort $(_HELP_VERBOSITY_ARGS)))))
|
|
122
|
+
_HELP_WORDS := list-scopes ls resolve r
|
|
123
|
+
_HELP_RESERVED_ARGS = $(_HELP_WORDS) $(_HELP_VERBOSITY_ARGS)
|
|
124
|
+
_HELP_SCOPES = $(strip $(filter-out $(_HELP_RESERVED_ARGS),$(_ARG_WORDS)))
|
|
125
|
+
_HAS_LIST_SCOPES_ARGUMENT = $(if $(filter list-scopes ls,$(_ARG_WORDS)),1)
|
|
126
|
+
_HAS_RESOLVE_ARGUMENT = $(if $(filter resolve r,$(_ARG_WORDS)),1)
|
|
127
|
+
|
|
128
|
+
_DEFAULT_GOAL := $(.DEFAULT_GOAL)
|
|
129
|
+
|
|
130
|
+
#--- includes
|
|
131
|
+
|
|
132
|
+
define _include_makefiles_in_directory
|
|
133
|
+
$(eval include $(filter-out $(abspath $(MAKEFILE_LIST)),$(wildcard $1/Makefile $1/*.mk)))
|
|
134
|
+
endef
|
|
135
|
+
|
|
136
|
+
_SEARCH_BASES := \
|
|
137
|
+
conf \
|
|
138
|
+
.local \
|
|
139
|
+
.local/conf
|
|
140
|
+
|
|
141
|
+
_INCLUDE_DIRECTORIES := $(CURDIR) \
|
|
142
|
+
$(foreach BASE,$(_SEARCH_BASES), \
|
|
143
|
+
$(CURDIR)/$(BASE) \
|
|
144
|
+
$(CURDIR)/$(BASE)/make \
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
$(foreach INCLUDE_DIRECTORY,$(_INCLUDE_DIRECTORIES), \
|
|
148
|
+
$(call _include_makefiles_in_directory,$(INCLUDE_DIRECTORY)) \
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
_CONSUMER_MAKEFILES := $(filter-out $(_BRNSHKR_CONFIG_MAKEFILE),$(MAKEFILE_LIST))
|
|
152
|
+
|
|
153
|
+
#---vvv tools
|
|
154
|
+
|
|
155
|
+
SHELL := $(if $(filter-out default,$(origin SHELL)),$(SHELL),/bin/sh)#~~ shell the recipes run in
|
|
156
|
+
CP ?= cp#~~ path to `cp` binary
|
|
157
|
+
CAT ?= cat#~~ path to `cat` binary
|
|
158
|
+
RM := $(if $(filter-out default,$(origin RM)),$(RM),rm)#~~ path to `rm` binary
|
|
159
|
+
MKDIR ?= mkdir#~~ path to `mkdir` binary
|
|
160
|
+
MV ?= mv#~~ path to `mv` binary
|
|
161
|
+
CMP ?= cmp#~~ path to `cmp` binary
|
|
162
|
+
CHMOD ?= chmod#~~ path to `chmod` binary
|
|
163
|
+
LN ?= ln#~~ path to `ln` binary
|
|
164
|
+
PRINTF ?= printf#~~ path to `printf` binary
|
|
165
|
+
AWK ?= awk#~~ path to `awk` binary
|
|
166
|
+
PHP ?= $(_RUN) php#~~ command that runs `php`
|
|
167
|
+
TAR ?= tar#~~ path to `tar` binary
|
|
168
|
+
GIT ?= git#~~ path to `git` binary
|
|
169
|
+
MKTEMP ?= mktemp#~~ path to `mktemp` binary
|
|
170
|
+
SCRIPT ?= script#~~ path to `script` binary
|
|
171
|
+
|
|
172
|
+
#---v settings
|
|
173
|
+
|
|
174
|
+
RUN ?= #~~ command the tools are run through, such as `docker compose exec <service>`
|
|
175
|
+
_RUN = $(if $(wildcard /.dockerenv),,$(RUN))
|
|
176
|
+
|
|
177
|
+
WORKDIR ?= $(if $(_RUN),/app,$(CURDIR))#~~ where the tools see the sources, which differs from `CURDIR` in a container
|
|
178
|
+
HOST_UID ?= $(or $(shell id -u 2>/dev/null),1000)#vv #~~ user a container writes the bind mount as
|
|
179
|
+
HOST_GID ?= $(or $(shell id -g 2>/dev/null),1000)#vv #~~ group a container writes the bind mount as
|
|
180
|
+
|
|
181
|
+
TARGET_PREFIX ?= #~~ namespaces every target this file defines, so `brnshkr` gives `brnshkr-lint`
|
|
182
|
+
_TARGET_PREFIX = $(if $(TARGET_PREFIX),$(TARGET_PREFIX)$(if $(filter %- %.,$(TARGET_PREFIX)),,-))
|
|
183
|
+
|
|
184
|
+
COLLISION_PREFIX ?= brnshkr#~~ namespace given to a shared target whose name this project already uses
|
|
185
|
+
_COLLISION_PREFIX = $(if $(COLLISION_PREFIX),$(COLLISION_PREFIX)$(if $(filter %- %.,$(COLLISION_PREFIX)),,-))
|
|
186
|
+
|
|
187
|
+
define _MANIFEST_NAME_AWK
|
|
188
|
+
/^[ \t]*[{]?[ \t]*"name"[ \t]*:/ {
|
|
189
|
+
match($$0, /^[ \t]*/)
|
|
190
|
+
if (found_name == "" || RLENGTH < indent) { \
|
|
191
|
+
indent = RLENGTH; split($$0, parts, "\""); \
|
|
192
|
+
found_name = parts[4] \
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
END { print found_name }
|
|
196
|
+
endef
|
|
197
|
+
|
|
198
|
+
ifneq ($(filter undefined,$(origin VENDOR) $(origin PACKAGE)),) # manifest
|
|
199
|
+
_MANIFEST := $(firstword $(wildcard $(CURDIR)/package.json $(CURDIR)/composer.json))
|
|
200
|
+
_MANIFEST_NAME := $(if $(_MANIFEST),$(shell $(AWK) '$(_MANIFEST_NAME_AWK)' $(_MANIFEST)))
|
|
201
|
+
endif # /manifest
|
|
202
|
+
|
|
203
|
+
VENDOR ?= $(if $(findstring /,$(_MANIFEST_NAME)),$(firstword $(subst /, ,$(_MANIFEST_NAME))))#~~ vendor name, taken from the manifest when unset
|
|
204
|
+
PACKAGE ?= $(or $(lastword $(subst /, ,$(_MANIFEST_NAME))),$(notdir $(CURDIR)))#~~ package name, taken from the manifest or the directory when unset
|
|
205
|
+
VERSION ?= 0.0.0-dev#vv #~~ package version
|
|
206
|
+
_LABEL = $(VENDOR)$(if $(and $(VENDOR),$(PACKAGE)),/)$(PACKAGE)
|
|
207
|
+
|
|
208
|
+
DEBUG ?= #~~ whether the commands are echoed before they run
|
|
209
|
+
unexport DEBUG # NOTICE: the `debug` package reads this from the environment
|
|
210
|
+
DEBUG_PREFIX ?= $(_ANNOUNCE)$(if $(or $(_IS_DEBUG),$(_IS_TRACE)),,@)#vvv #~~ prefix for a command
|
|
211
|
+
_IS_DEBUG = $(filter-out $(_FALSE),$(DEBUG))
|
|
212
|
+
|
|
213
|
+
TRACE ?= #~~ whether the guards are echoed too, which `DEBUG` leaves out
|
|
214
|
+
TRACE_PREFIX ?= $(if $(_IS_TRACE),,@)#vvv #~~ prefix for a guard command
|
|
215
|
+
_IS_TRACE = $(filter-out $(_FALSE),$(TRACE))
|
|
216
|
+
|
|
217
|
+
ANNOUNCEMENT ?= Running `%s`#~~ message a verb prints before each target it runs, `%s` being the target, empty for none
|
|
218
|
+
_ANNOUNCE = $(if $(and $(_IS_ANNOUNCING),$(ANNOUNCEMENT),$(filter-out _% %-group $(_ANNOUNCED),$@)),$(eval _ANNOUNCED += $@)$(info $(shell $(call log,$(ANNOUNCEMENT),$(COLOR_NOTICE),'$@'))))
|
|
219
|
+
|
|
220
|
+
_FALSE := 0 false off no
|
|
221
|
+
_TRUE := 1 true on yes
|
|
222
|
+
_IS_CI = $(filter-out $(_FALSE),$(CI))
|
|
223
|
+
|
|
224
|
+
_MAKE_FLAGS = $(if $(or $(_IS_DEBUG),$(_IS_TRACE)),--no-print-directory,-s) _IS_ANNOUNCING=
|
|
225
|
+
_VERB_FLAGS = $(_MAKE_FLAGS) $(_INHERITED_FLAGS) $(if $(_IS_PARALLEL),--output-sync=recurse) --keep-going _IS_ANNOUNCING=1
|
|
226
|
+
_IS_PARALLEL = $(filter-out -j1,$(filter -j%,$(MAKEFLAGS)))
|
|
227
|
+
|
|
228
|
+
_UNEXPORTED_VARIABLES = COMPOSER DEBUG ESLINT_FLAGS VITEST
|
|
229
|
+
|
|
230
|
+
_INHERITED_FLAGS = $(foreach VARIABLE,$(_UNEXPORTED_VARIABLES), \
|
|
231
|
+
$(if $(filter environment,$(origin $(VARIABLE))),$(VARIABLE)='$($(VARIABLE))'))
|
|
232
|
+
|
|
233
|
+
BRNSHKR_CONFIG_ERROR_CODE ?= 69#vvv #~~ the error code for custom errors thrown by this Makefile
|
|
234
|
+
|
|
235
|
+
#---vv theming
|
|
236
|
+
|
|
237
|
+
INDENT ?= 1#~~ indent used in `make help` output
|
|
238
|
+
VALUE_WIDTH ?= 48#~~ widest a value column gets in `make help`, past which a value is truncated
|
|
239
|
+
|
|
240
|
+
NO_ANSI ?= #~~ whether ANSI escapes are suppressed
|
|
241
|
+
_IS_NO_ANSI = $(filter-out $(_FALSE),$(NO_ANSI))
|
|
242
|
+
_NO_ANSI_OPTION = $(if $(_IS_NO_ANSI),--no-ansi)
|
|
243
|
+
|
|
244
|
+
THEME ?= #~~ the theme to use
|
|
245
|
+
THEME_BRNSHKR := brnshkr
|
|
246
|
+
THEME_SYMFONY := symfony
|
|
247
|
+
|
|
248
|
+
_THEMES := $(THEME_BRNSHKR) \
|
|
249
|
+
$(THEME_SYMFONY)
|
|
250
|
+
|
|
251
|
+
_THEME_DEFAULT := $(THEME_BRNSHKR)
|
|
252
|
+
_THEME := $(if $(filter $(THEME),$(_THEMES)),$(THEME),$(_THEME_DEFAULT))
|
|
253
|
+
|
|
254
|
+
EDITOR_URL ?= #~~ URL template the entries link to, in `{cwd}`, `{file}`, `{line}` and `{wslDistro}`
|
|
255
|
+
EDITOR_VSCODE := vscode
|
|
256
|
+
EDITOR_PHPSTORM := phpstorm
|
|
257
|
+
|
|
258
|
+
_EDITORS := $(EDITOR_VSCODE) \
|
|
259
|
+
$(EDITOR_PHPSTORM)
|
|
260
|
+
|
|
261
|
+
_EDITOR_URL_VSCODE := vscode://file/{file}:{line}
|
|
262
|
+
_EDITOR_URL_VSCODE_WSL := vscode://vscode-remote/wsl+{wslDistro}{file}:{line}
|
|
263
|
+
_EDITOR_URL_PHPSTORM := phpstorm://open?file={file}&line={line}
|
|
264
|
+
_DETECTED_EDITOR = $(or $(call _editor_from_env),$(call _editor_from_command))
|
|
265
|
+
|
|
266
|
+
EDITOR ?= $(_DETECTED_EDITOR)#~~ the editor to use, empty for no links
|
|
267
|
+
_EDITOR = $(or $(filter $(EDITOR),$(_EDITORS)),$(if $(strip $(EDITOR)),$(if $(findstring environment,$(origin EDITOR)),$(_DETECTED_EDITOR))))
|
|
268
|
+
_EDITOR_UNKNOWN = $(if $(_EDITOR)$(findstring environment,$(origin EDITOR)),,$(strip $(EDITOR)))
|
|
269
|
+
|
|
270
|
+
_EDITOR_URL = $(subst {wslDistro},$(WSL_DISTRO_NAME),$(subst {cwd},$(CURDIR),$(or \
|
|
271
|
+
$(EDITOR_URL), \
|
|
272
|
+
$(if $(filter $(EDITOR_VSCODE),$(_EDITOR)),$(if $(WSL_DISTRO_NAME),$(_EDITOR_URL_VSCODE_WSL),$(_EDITOR_URL_VSCODE))), \
|
|
273
|
+
$(if $(filter $(EDITOR_PHPSTORM),$(_EDITOR)),$(_EDITOR_URL_PHPSTORM)) \
|
|
274
|
+
)))
|
|
275
|
+
|
|
276
|
+
_INLINE_CODE_URL = $(if $(_IS_NO_ANSI),,$(_EDITOR_URL))
|
|
277
|
+
|
|
278
|
+
ifeq ($(origin LOGO),undefined) # logo
|
|
279
|
+
define LOGO
|
|
280
|
+
___ __ __
|
|
281
|
+
/ _ )_______ ____/ / / /__ ____
|
|
282
|
+
/ _ / __/ _ \\(_--/ _ \\/ ´_// __/
|
|
283
|
+
/____/_/ /_//_/___/_//_/_/\\_\\/_/
|
|
284
|
+
|
|
285
|
+
endef
|
|
286
|
+
endif # /logo
|
|
287
|
+
|
|
288
|
+
#---- colors
|
|
289
|
+
|
|
290
|
+
COLOR_NORMAL := normal
|
|
291
|
+
COLOR_BLACK := black
|
|
292
|
+
COLOR_RED := red
|
|
293
|
+
COLOR_GREEN := green
|
|
294
|
+
COLOR_YELLOW := yellow
|
|
295
|
+
COLOR_BLUE := blue
|
|
296
|
+
COLOR_MAGENTA := magenta
|
|
297
|
+
COLOR_CYAN := cyan
|
|
298
|
+
COLOR_WHITE := white
|
|
299
|
+
|
|
300
|
+
ifeq ($(_THEME),$(THEME_BRNSHKR)) # theme
|
|
301
|
+
COLOR_PRIMARY ?= $(COLOR_CYAN)
|
|
302
|
+
COLOR_SECONDARY ?= $(COLOR_MAGENTA)
|
|
303
|
+
COLOR_TERTIARY ?= $(COLOR_BLACK) $(MODIFIER_BRIGHT)
|
|
304
|
+
COLOR_ERROR ?= $(COLOR_RED)
|
|
305
|
+
COLOR_WARNING ?= $(COLOR_YELLOW)
|
|
306
|
+
COLOR_NOTICE ?= $(COLOR_MAGENTA)
|
|
307
|
+
COLOR_SUCCESS ?= $(COLOR_GREEN)
|
|
308
|
+
COLOR_LOGO ?= $(COLOR_SECONDARY)
|
|
309
|
+
COLOR_TITLE ?= $(COLOR_PRIMARY) $(MODIFIER_UNDERLINE)
|
|
310
|
+
COLOR_USAGE_ARGUMENTS ?= $(COLOR_PRIMARY)
|
|
311
|
+
COLOR_USAGE_PROGRAM ?= $(COLOR_PRIMARY)
|
|
312
|
+
COLOR_SECTION_LABEL ?= $(COLOR_SECONDARY)
|
|
313
|
+
COLOR_SCOPE ?= $(COLOR_SECONDARY)
|
|
314
|
+
COLOR_ENTRY ?= $(COLOR_PRIMARY)
|
|
315
|
+
COLOR_DESCRIPTION ?= $(COLOR_TERTIARY)
|
|
316
|
+
COLOR_INLINE_CODE ?= $(COLOR_SECONDARY)
|
|
317
|
+
COLOR_HIGHLIGHT ?= $(COLOR_YELLOW)
|
|
318
|
+
else ifeq ($(_THEME),$(THEME_SYMFONY))
|
|
319
|
+
COLOR_PRIMARY ?= $(COLOR_GREEN)
|
|
320
|
+
COLOR_SECONDARY ?= $(COLOR_YELLOW)
|
|
321
|
+
COLOR_TERTIARY ?= $(COLOR_NORMAL)
|
|
322
|
+
COLOR_ERROR ?= $(COLOR_RED)
|
|
323
|
+
COLOR_WARNING ?= $(COLOR_YELLOW)
|
|
324
|
+
COLOR_NOTICE ?= $(COLOR_CYAN)
|
|
325
|
+
COLOR_SUCCESS ?= $(COLOR_GREEN)
|
|
326
|
+
COLOR_LOGO ?= $(COLOR_SECONDARY)
|
|
327
|
+
COLOR_TITLE ?= $(COLOR_PRIMARY) $(MODIFIER_UNDERLINE)
|
|
328
|
+
COLOR_USAGE_ARGUMENTS ?= $(COLOR_TERTIARY)
|
|
329
|
+
COLOR_USAGE_PROGRAM ?= $(COLOR_TERTIARY)
|
|
330
|
+
COLOR_SECTION_LABEL ?= $(COLOR_SECONDARY)
|
|
331
|
+
COLOR_SCOPE ?= $(COLOR_SECONDARY)
|
|
332
|
+
COLOR_ENTRY ?= $(COLOR_PRIMARY)
|
|
333
|
+
COLOR_DESCRIPTION ?= $(COLOR_TERTIARY)
|
|
334
|
+
COLOR_INLINE_CODE ?= $(COLOR_SECONDARY)
|
|
335
|
+
COLOR_HIGHLIGHT ?= $(COLOR_YELLOW)
|
|
336
|
+
endif # /theme
|
|
337
|
+
|
|
338
|
+
_COLORS := $(COLOR_NORMAL) \
|
|
339
|
+
$(COLOR_WHITE) \
|
|
340
|
+
$(COLOR_BLACK) \
|
|
341
|
+
$(COLOR_RED) \
|
|
342
|
+
$(COLOR_YELLOW) \
|
|
343
|
+
$(COLOR_GREEN) \
|
|
344
|
+
$(COLOR_CYAN) \
|
|
345
|
+
$(COLOR_BLUE) \
|
|
346
|
+
$(COLOR_MAGENTA)
|
|
347
|
+
|
|
348
|
+
#----vvv modifiers
|
|
349
|
+
|
|
350
|
+
MODIFIER_NORMAL := normal
|
|
351
|
+
MODIFIER_UNDERLINE := underline
|
|
352
|
+
MODIFIER_BRIGHT := bright
|
|
353
|
+
MODIFIER_BOLD := bold
|
|
354
|
+
MODIFIER_REVERSE := reverse
|
|
355
|
+
|
|
356
|
+
#---vv arguments
|
|
357
|
+
|
|
358
|
+
_TARGET_SOURCES := $(shell $(AWK) -v _PREFIX='$(_TARGET_PREFIX)' -v _CURDIR='$(CURDIR)' \
|
|
359
|
+
'function does_path_exist(path, line, does_exist) { does_exist = (getline line < path) >= 0; close(path); return does_exist } \
|
|
360
|
+
/^ifneq/ && index($$0, "wildcard") { \
|
|
361
|
+
depth += 1; \
|
|
362
|
+
if (!skip) { \
|
|
363
|
+
count = split(substr($$0, index($$0, "wildcard") + 9), paths, /[[:space:]]+/); \
|
|
364
|
+
has_existing_path = 0; \
|
|
365
|
+
for (i = 1; i <= count; i += 1) { \
|
|
366
|
+
path = paths[i]; \
|
|
367
|
+
if (!index(path, "/")) continue; \
|
|
368
|
+
path = substr(path, index(path, "/")); \
|
|
369
|
+
sub(/[^A-Za-z0-9_.\/-]+$$/, "", path); \
|
|
370
|
+
if (does_path_exist(_CURDIR path)) has_existing_path = 1; \
|
|
371
|
+
} \
|
|
372
|
+
if (!has_existing_path) skip = depth \
|
|
373
|
+
} \
|
|
374
|
+
next \
|
|
375
|
+
} \
|
|
376
|
+
/^ifdef|^ifndef|^ifeq|^ifneq/ { depth += 1; next } \
|
|
377
|
+
/^endif/ { if (skip == depth) skip = 0; depth -= 1; next } \
|
|
378
|
+
skip { next } \
|
|
379
|
+
{ while (match($$0, /\$$\(call _target,[a-zA-Z0-9_.-]+\)/)) \
|
|
380
|
+
$$0 = substr($$0, 1, RSTART - 1) "@" substr($$0, RSTART + 15, RLENGTH - 16) substr($$0, RSTART + RLENGTH) } \
|
|
381
|
+
{ gsub(/\$$\(_TARGET_PREFIX\)/, _PREFIX) } \
|
|
382
|
+
/^@?[a-zA-Z0-9_.-]+([[:space:]]+@?[a-zA-Z0-9_.-]+)*:([^=]|$$)/ { \
|
|
383
|
+
count = split(substr($$0, 1, index($$0, ":") - 1), ns, /[[:space:]]+/); \
|
|
384
|
+
for (i = 1; i <= count; i += 1) \
|
|
385
|
+
if (ns[i] != "" && ns[i] !~ /^[.]/) printf "%s|%s:%d ", ns[i], FILENAME, FNR \
|
|
386
|
+
}' \
|
|
387
|
+
$(MAKEFILE_LIST) \
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
_TARGET_FILES := $(foreach SOURCE,$(_TARGET_SOURCES),$(firstword $(subst :, ,$(SOURCE))))
|
|
391
|
+
_TARGET_LINKS := $(subst |,=,$(patsubst @%,%,$(_TARGET_SOURCES)))
|
|
392
|
+
|
|
393
|
+
ifeq ($(_TARGET_SOURCES),) # awk
|
|
394
|
+
$(error [$(_LABEL)] `$(AWK)` found no targets, so it is missing or is not an awk)
|
|
395
|
+
endif # /awk
|
|
396
|
+
|
|
397
|
+
#**
|
|
398
|
+
#* The targets a set of files defines.
|
|
399
|
+
#*
|
|
400
|
+
#* parameters:
|
|
401
|
+
#* files: list<string>
|
|
402
|
+
#*
|
|
403
|
+
#* returns: list<string>
|
|
404
|
+
#*
|
|
405
|
+
define _targets_from
|
|
406
|
+
$(sort $(foreach FILE,$1,$(patsubst %|$(FILE),%,$(filter %|$(FILE),$(_TARGET_FILES)))))
|
|
407
|
+
endef
|
|
408
|
+
|
|
409
|
+
#**
|
|
410
|
+
#* Where a target is defined, each as `<file>:<line>`.
|
|
411
|
+
#*
|
|
412
|
+
#* parameters:
|
|
413
|
+
#* target: string
|
|
414
|
+
#*
|
|
415
|
+
#* returns: list<string>
|
|
416
|
+
#*
|
|
417
|
+
define _definitions_of
|
|
418
|
+
$(sort $(patsubst $1|%,%,$(filter $1|%,$(_TARGET_SOURCES))) $(patsubst @$1|%,%,$(filter @$1|%,$(_TARGET_SOURCES))))
|
|
419
|
+
endef
|
|
420
|
+
|
|
421
|
+
#**
|
|
422
|
+
#* The files that define a target, which is what a collision has to name.
|
|
423
|
+
#*
|
|
424
|
+
#* parameters:
|
|
425
|
+
#* target: string
|
|
426
|
+
#*
|
|
427
|
+
#* returns: list<string>
|
|
428
|
+
#*
|
|
429
|
+
define _files_defining
|
|
430
|
+
$(sort $(foreach DEFINITION,$(call _definitions_of,$1),$(firstword $(subst :, ,$(DEFINITION)))))
|
|
431
|
+
endef
|
|
432
|
+
|
|
433
|
+
#**
|
|
434
|
+
#* The name a shared target ends up with.
|
|
435
|
+
#* Its own, unless this project took that name, in which case the first free one behind
|
|
436
|
+
#* `COLLISION_PREFIX`, numbered if the project took that too.
|
|
437
|
+
#*
|
|
438
|
+
#* parameters:
|
|
439
|
+
#* name: string
|
|
440
|
+
#*
|
|
441
|
+
#* returns: string
|
|
442
|
+
#*
|
|
443
|
+
define _target
|
|
444
|
+
$(or $(firstword $(filter-out $(_CONSUMER_TARGETS),$(_TARGET_PREFIX)$1 $(_COLLISION_PREFIX)$(_TARGET_PREFIX)$1 $(foreach \
|
|
445
|
+
NUMBER,1 2 3 4 5 6 7 8 9,$(_COLLISION_PREFIX)$(_TARGET_PREFIX)$1-$(NUMBER)))),$(_COLLISION_PREFIX)$(_TARGET_PREFIX)$1)
|
|
446
|
+
endef
|
|
447
|
+
|
|
448
|
+
_CONSUMER_TARGETS := $(call _targets_from,$(_CONSUMER_MAKEFILES))
|
|
449
|
+
_SCRAPED_SHARED := $(call _targets_from,$(filter-out $(_CONSUMER_MAKEFILES),$(MAKEFILE_LIST)))
|
|
450
|
+
_SHARED_NAMES := $(patsubst @%,%,$(filter @%,$(_SCRAPED_SHARED)))
|
|
451
|
+
_COLLIDING_TARGETS := $(strip $(foreach NAME,$(_SHARED_NAMES),$(if $(filter $(_TARGET_PREFIX)$(NAME),$(_CONSUMER_TARGETS)),$(NAME))))
|
|
452
|
+
_TARGET_NAMES := $(foreach NAME,$(_SHARED_NAMES),$(NAME)=$(call _target,$(NAME)))
|
|
453
|
+
_SHARED_TARGETS := $(sort $(filter-out @%,$(_SCRAPED_SHARED)) $(foreach NAME,$(_SHARED_NAMES),$(call _target,$(NAME))))
|
|
454
|
+
_ALL_TARGETS := $(sort $(_CONSUMER_TARGETS) $(_SHARED_TARGETS))
|
|
455
|
+
_DUPLICATE_TARGETS := $(strip $(foreach NAME,$(_CONSUMER_TARGETS),$(if $(word 2,$(filter $(_CONSUMER_MAKEFILES),$(call _files_defining,$(NAME)))),$(NAME))))
|
|
456
|
+
.DEFAULT_GOAL := $(or $(_DEFAULT_GOAL),$(call _target,help))
|
|
457
|
+
|
|
458
|
+
TARGET := $(firstword $(filter $(_ALL_TARGETS),$(MAKECMDGOALS)))#~~ the first target of this run
|
|
459
|
+
ARG1 = $(word 1,$(ARGS))#~~ the running target's first argument, and so on through `ARG9`
|
|
460
|
+
ARG2 = $(word 2,$(ARGS))
|
|
461
|
+
ARG3 = $(word 3,$(ARGS))
|
|
462
|
+
ARG4 = $(word 4,$(ARGS))
|
|
463
|
+
ARG5 = $(word 5,$(ARGS))
|
|
464
|
+
ARG6 = $(word 6,$(ARGS))
|
|
465
|
+
ARG7 = $(word 7,$(ARGS))
|
|
466
|
+
ARG8 = $(word 8,$(ARGS))
|
|
467
|
+
ARG9 = $(word 9,$(ARGS))
|
|
468
|
+
ARGS = $(call _shell_quoted,$(_ARGS))#~~ every argument the running target was handed, quoted for the shell
|
|
469
|
+
|
|
470
|
+
_ARGS = $(if $(filter command line,$(origin ARGS)),$(ARGS),$(strip $(filter-out $(_ALL_TARGETS),$(call _collect_args,$@,$(MAKECMDGOALS)))))
|
|
471
|
+
_FIXER_ARGS = $(call _shell_quoted,$(call _without_dry_run,$(_ARGS)))
|
|
472
|
+
|
|
473
|
+
#**
|
|
474
|
+
#* The goal a name reaches, empty where it names nothing.
|
|
475
|
+
#*
|
|
476
|
+
#* parameters:
|
|
477
|
+
#* name: string
|
|
478
|
+
#*
|
|
479
|
+
#* returns: string
|
|
480
|
+
#*
|
|
481
|
+
define _goal_of
|
|
482
|
+
$(or $(filter $1,$(_ALL_TARGETS)),$(filter $(call _target,$1),$(_ALL_TARGETS)))
|
|
483
|
+
endef
|
|
484
|
+
|
|
485
|
+
TARGET_ALIASES ?= h=help \
|
|
486
|
+
c=check \
|
|
487
|
+
t=test \
|
|
488
|
+
f=fix#~~ short names for targets, as `<alias>=<target>` pairs
|
|
489
|
+
|
|
490
|
+
_TARGET_ALIAS_MAP = $(foreach PAIR,$(TARGET_ALIASES),$(lastword $(subst =, ,$(PAIR)))=$(firstword $(subst =, ,$(PAIR))))
|
|
491
|
+
|
|
492
|
+
_ALIAS_TARGET = $(strip $(foreach PAIR,$(TARGET_ALIASES), \
|
|
493
|
+
$(if $(filter $(patsubst -%,%,$@),$(firstword $(subst =, ,$(PAIR)))),$(lastword $(subst =, ,$(PAIR))))))
|
|
494
|
+
|
|
495
|
+
_ALIAS_GOAL = $(call _goal_of,$(_ALIAS_TARGET))
|
|
496
|
+
|
|
497
|
+
_TAKEN_ALIASES := $(strip $(foreach PAIR,$(TARGET_ALIASES), \
|
|
498
|
+
$(filter $(firstword $(subst =, ,$(PAIR))),$(_ALL_TARGETS))))
|
|
499
|
+
|
|
500
|
+
_MALFORMED_ALIASES := $(strip $(foreach PAIR,$(TARGET_ALIASES), \
|
|
501
|
+
$(if $(filter 2,$(words $(subst =, ,$(PAIR)))),,$(PAIR))))
|
|
502
|
+
|
|
503
|
+
_DANGLING_ALIASES := $(strip $(foreach PAIR,$(filter-out $(_MALFORMED_ALIASES),$(TARGET_ALIASES)), \
|
|
504
|
+
$(if $(call _goal_of,$(lastword $(subst =, ,$(PAIR)))),,$(PAIR))))
|
|
505
|
+
|
|
506
|
+
ifneq ($(_TAKEN_ALIASES),) # taken
|
|
507
|
+
$(error [$(_LABEL)] `$(firstword $(_TAKEN_ALIASES))` is a target, so it cannot also be an alias)
|
|
508
|
+
endif # /taken
|
|
509
|
+
|
|
510
|
+
ifneq ($(_MALFORMED_ALIASES),) # malformed
|
|
511
|
+
$(error [$(_LABEL)] `$(firstword $(_MALFORMED_ALIASES))` is not an `<alias>=<target>` pair)
|
|
512
|
+
endif # /malformed
|
|
513
|
+
|
|
514
|
+
ifneq ($(_DANGLING_ALIASES),) # dangling
|
|
515
|
+
$(error [$(_LABEL)] `$(subst =,` aliases `,$(firstword $(_DANGLING_ALIASES)))`, which is not a target)
|
|
516
|
+
endif # /dangling
|
|
517
|
+
|
|
518
|
+
_IS_JUST_PRINT = $(findstring n,$(firstword $(MAKEFLAGS)))
|
|
519
|
+
_IS_FIRST_GOAL = $(filter $@,$(firstword $(MAKECMDGOALS)))
|
|
520
|
+
_UNKNOWN_ARGS = $(call _shell_quoted,$(filter-out $@,$(_ARGS)))
|
|
521
|
+
_SUGGESTED_FOR = $@
|
|
522
|
+
_TARGET_GOALS = $(filter-out _% -%,$(_ALL_TARGETS))
|
|
523
|
+
_STAGE_GOALS = $(foreach STAGE,$(_DOTENV_STAGES),$(addprefix $(STAGE)-,$(_TARGET_GOALS)))
|
|
524
|
+
|
|
525
|
+
#**
|
|
526
|
+
#* The goals tied for nearest to what was typed, at most three.
|
|
527
|
+
#*
|
|
528
|
+
#* parameters:
|
|
529
|
+
#* goals: list<string>
|
|
530
|
+
#* typed_name?: string = $(_SUGGESTED_FOR)
|
|
531
|
+
#*
|
|
532
|
+
#* returns: list<string>
|
|
533
|
+
#*
|
|
534
|
+
define _suggestions
|
|
535
|
+
$(shell $(PRINTF) '%s\n' $1 \
|
|
536
|
+
| $(AWK) -v _UNKNOWN='$(or $2,$(_SUGGESTED_FOR))' ' \
|
|
537
|
+
function levenshtein(left, right, left_length, right_length, left_index, right_index, cost, best, previous, current) { \
|
|
538
|
+
left_length = length(left); \
|
|
539
|
+
right_length = length(right); \
|
|
540
|
+
for (right_index = 0; right_index <= right_length; right_index += 1) previous[right_index] = right_index; \
|
|
541
|
+
for (left_index = 1; left_index <= left_length; left_index += 1) { \
|
|
542
|
+
current[0] = left_index; \
|
|
543
|
+
for (right_index = 1; right_index <= right_length; right_index += 1) { \
|
|
544
|
+
cost = substr(left, left_index, 1) == substr(right, right_index, 1) ? 0 : 1; \
|
|
545
|
+
best = previous[right_index] + 1; \
|
|
546
|
+
if (current[right_index - 1] + 1 < best) best = current[right_index - 1] + 1; \
|
|
547
|
+
if (previous[right_index - 1] + cost < best) best = previous[right_index - 1] + cost; \
|
|
548
|
+
current[right_index] = best \
|
|
549
|
+
} \
|
|
550
|
+
for (right_index = 0; right_index <= right_length; right_index += 1) previous[right_index] = current[right_index]; \
|
|
551
|
+
} \
|
|
552
|
+
return previous[right_length] \
|
|
553
|
+
} \
|
|
554
|
+
{ measured = levenshtein(_UNKNOWN, $$0); if (measured <= (length(_UNKNOWN) > 4 ? 2 : 1) || (length(_UNKNOWN) >= 3 && index($$0, _UNKNOWN))) printf "%d %s\n", measured, $$0 }' \
|
|
555
|
+
| sort -n \
|
|
556
|
+
| $(AWK) 'NR == 1 { nearest = $$1 } $$1 == nearest && NR <= 3 { print $$2 }')
|
|
557
|
+
endef
|
|
558
|
+
|
|
559
|
+
_SUGGESTED_TARGETS = $(if $(filter -%,$(_SUGGESTED_FOR)),,$(or \
|
|
560
|
+
$(call _suggestions,$(_TARGET_GOALS)),$(call _suggestions,$(_STAGE_GOALS))))
|
|
561
|
+
|
|
562
|
+
_ARG_VALUES =
|
|
563
|
+
_UNKNOWN_VALUES = $(if $(_ARG_VALUES),$(filter-out $(_ARG_VALUES),$(_ARG_WORDS)))
|
|
564
|
+
_MISTYPED_VALUE = $(firstword $(_UNKNOWN_VALUES))
|
|
565
|
+
_SUGGESTED_VALUES = $(call _suggestions,$(_ARG_VALUES),$(_MISTYPED_VALUE))
|
|
566
|
+
|
|
567
|
+
#--- common
|
|
568
|
+
|
|
569
|
+
_COMMA := ,
|
|
570
|
+
_QUOTE := '
|
|
571
|
+
_SPACE := $(subst ,, )
|
|
572
|
+
|
|
573
|
+
define _NEWLINE
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
endef
|
|
577
|
+
|
|
578
|
+
_PACKAGE_CONF_DIRS = $(WORKDIR)/vendor/brnshkr/config/conf \
|
|
579
|
+
$(WORKDIR)/node_modules/@brnshkr/config/conf
|
|
580
|
+
|
|
581
|
+
_AUTOLOADER = $(WORKDIR)/vendor/autoload.php
|
|
582
|
+
_TRACKED_EXTENSIONS := $(sort $(suffix $(shell $(GIT) ls-files 2>/dev/null)))
|
|
583
|
+
_FIXER_NAMES :=
|
|
584
|
+
_ANALYZER_NAMES :=
|
|
585
|
+
_GROUP_NAMES :=
|
|
586
|
+
_TEST_NAMES :=
|
|
587
|
+
_COVERAGE_NAMES :=
|
|
588
|
+
_PACK_NAMES :=
|
|
589
|
+
_PLAIN_CONFIGS = $(WORKDIR)/.gitignore
|
|
590
|
+
_LAYERED_CONFIGS =
|
|
591
|
+
_LINKED_CONFIGS =
|
|
592
|
+
|
|
593
|
+
_PLACED_CONFIGS = $(WORKDIR)/.editorconfig|$(_BRNSHKR_CONFIG_DIR)/editorconfig.dist \
|
|
594
|
+
$(WORKDIR)/.vscode/settings.json|$(_BRNSHKR_CONFIG_DIR)/vscode-settings.dist.jsonc \
|
|
595
|
+
$(WORKDIR)/.vscode/extensions.json|$(_BRNSHKR_CONFIG_DIR)/vscode-extensions.dist.json \
|
|
596
|
+
$(WORKDIR)/.vscode/css-custom-data.json|$(_BRNSHKR_CONFIG_DIR)/vscode-css-custom-data.dist.json \
|
|
597
|
+
$(if $(wildcard $(CURDIR)/package.json),$(WORKDIR)/bunfig.toml|$(_BRNSHKR_CONFIG_DIR)/bunfig.dist.toml) \
|
|
598
|
+
$(if $(wildcard $(CURDIR)/composer.json),$(WORKDIR)/.gitattributes|$(_BRNSHKR_CONFIG_DIR)/gitattributes.dist)
|
|
599
|
+
|
|
600
|
+
$(call _target,help): #~~ show this help, narrowed by any scope named and widened by `v`, `vv` or `vvv`
|
|
601
|
+
$(DEBUG_PREFIX)$(call _make_vars_as_env) \
|
|
602
|
+
_CURDIR="$(abspath $(CURDIR))" \
|
|
603
|
+
_WORKDIR="$(WORKDIR)" \
|
|
604
|
+
_TARGET_PREFIX="$(_TARGET_PREFIX)" \
|
|
605
|
+
_HEADER_LOGO="$(if $(LOGO),$$($(PRINTF) '%b' '$(call _color,$(COLOR_LOGO))$(subst $(_NEWLINE),\n,$(LOGO))$(_ANSI_RESET)__BRNSHKR_LOGO_END__'),)" \
|
|
606
|
+
_HEADER_TITLE="$(if $(or $(PACKAGE),$(VERSION)),$$($(PRINTF) '%b' '$(call text,$(if $(PACKAGE),$(call _to_title_case,$(PACKAGE)))$(if $(and $(PACKAGE),$(VERSION)), )$(if $(VERSION),($(strip $(VERSION)))),$(COLOR_TITLE))'),)" \
|
|
607
|
+
_HEADER_USAGE="$$($(PRINTF) '%b' '$(call text,Usage:,$(COLOR_SECTION_LABEL))\n $(call text,make,$(COLOR_USAGE_PROGRAM)) $(call text,<command> [--] [arguments],$(COLOR_USAGE_ARGUMENTS))')" \
|
|
608
|
+
_SCOPES="$(subst .,/,$(_HELP_SCOPES))" \
|
|
609
|
+
_HAS_LIST_SCOPES_ARGUMENT="$(_HAS_LIST_SCOPES_ARGUMENT)" \
|
|
610
|
+
_HAS_RESOLVE_ARGUMENT="$(_HAS_RESOLVE_ARGUMENT)" \
|
|
611
|
+
_INLINE_CODE_OPEN="$$($(PRINTF) '%b' '$(call _ansi,$(COLOR_INLINE_CODE))')" \
|
|
612
|
+
_INLINE_CODE_CLOSE="$$($(PRINTF) '%b' '$(_ANSI_RESET)$(call _ansi,$(COLOR_DESCRIPTION))')" \
|
|
613
|
+
_VERBOSITY="$(or $(V),$(_HELP_VERBOSITY))" \
|
|
614
|
+
LC_ALL=C \
|
|
615
|
+
$(AWK) "$$_HELP_AWK" \
|
|
616
|
+
$(MAKEFILE_LIST)
|
|
617
|
+
|
|
618
|
+
STARTUP_TARGETS ?= #v #~~ this project's own targets, run by `startup` after the dependencies
|
|
619
|
+
|
|
620
|
+
_STARTUP := _$(_TARGET_PREFIX)install-missing-hooks \
|
|
621
|
+
$(if $(_IS_INSTALLING),,$(if $(wildcard $(CURDIR)/composer.json),_$(_TARGET_PREFIX)composer-install) \
|
|
622
|
+
$(if $(wildcard $(CURDIR)/package.json),_$(_TARGET_PREFIX)bun-install))
|
|
623
|
+
|
|
624
|
+
$(call _target,startup): $(_STARTUP) #~~ brings a fresh checkout to a workable state #v
|
|
625
|
+
$(DEBUG_PREFIX)$(MAKE) $(_VERB_FLAGS) --no-keep-going $(call _target,configs)
|
|
626
|
+
$(DEBUG_PREFIX)$(if $(STARTUP_TARGETS),$(MAKE) $(_VERB_FLAGS) --no-keep-going $(STARTUP_TARGETS),:)
|
|
627
|
+
|
|
628
|
+
GIT_HOOKS ?= #v #~~ git hooks this project installs, as `<hook>=<target>` pairs
|
|
629
|
+
|
|
630
|
+
$(call _target,install-hooks): #~~ installs this project's git hooks, replacing any that exist #v
|
|
631
|
+
$(TRACE_PREFIX)$(GIT) rev-parse --is-inside-work-tree >/dev/null 2>&1 || { \
|
|
632
|
+
$(call log,`install-hooks` needs a git checkout.,$(COLOR_ERROR)) >&2; \
|
|
633
|
+
exit $(BRNSHKR_CONFIG_ERROR_CODE); \
|
|
634
|
+
}
|
|
635
|
+
$(DEBUG_PREFIX)$(call _install_git_hooks,yes)
|
|
636
|
+
|
|
637
|
+
_$(_TARGET_PREFIX)install-missing-hooks:
|
|
638
|
+
$(DEBUG_PREFIX)$(call _install_git_hooks)
|
|
639
|
+
|
|
640
|
+
CONFIG ?= #v #~~ `local` or `dist` to pin which config every tool reads, rather than the first that is there
|
|
641
|
+
ARCHIVE_EXTRA_PATHS ?= #v #~~ paths an archive ships beside the autoload roots, a trailing `/` taking the whole tree
|
|
642
|
+
|
|
643
|
+
_PINNED_CONFIG = $(filter local dist,$(CONFIG))
|
|
644
|
+
_TEMPLATE_DIRS = $(_BRNSHKR_CONFIG_DIR) $(filter-out $(_BRNSHKR_CONFIG_DIR),$(wildcard $(CURDIR)/vendor/brnshkr/config/conf $(CURDIR)/node_modules/@brnshkr/config/conf))
|
|
645
|
+
|
|
646
|
+
_NAMED_CONFIGS = $(filter-out local l force f,$(_ARG_WORDS))
|
|
647
|
+
_IS_FORCED_CONFIG = $(filter force f,$(_ARG_WORDS))
|
|
648
|
+
_CONFIG_NAMES = $(sort $(foreach CONFIG,$(_CONFIGS) $(_PLACED_CONFIGS) $(_LINKED_CONFIGS),$(call _config_name,$(CONFIG))))
|
|
649
|
+
|
|
650
|
+
_CONFIGS = $(call _in_project,$(_PLAIN_CONFIGS) $(foreach CONFIG,$(_LAYERED_CONFIGS),$(call _tracked,$(CONFIG))) \
|
|
651
|
+
$(if $(filter local l,$(_ARG_WORDS)),$(subst .dist.,.,$(_LAYERED_CONFIGS))))
|
|
652
|
+
|
|
653
|
+
$(call _target,configs): #~~ writes the tool configs this project is missing, `local` its private ones too #v
|
|
654
|
+
$(TRACE_PREFIX)$(call _require_known_value,config)
|
|
655
|
+
$(DEBUG_PREFIX)for target in $(call _host_path,$(call _selected_configs,$(_CONFIGS))); do \
|
|
656
|
+
if [ -f "$$target" ]; then \
|
|
657
|
+
$(if $(_IS_FORCED_CONFIG),:,$(call log,`%s` already exists.,$(COLOR_NOTICE),"./$${target#$(CURDIR)/}")); \
|
|
658
|
+
$(if $(_IS_FORCED_CONFIG),:,$(if $(_NAMED_CONFIGS),$(call confirm,Overwrite it?),false)) || continue; \
|
|
659
|
+
fi; \
|
|
660
|
+
name="$${target##*/}"; \
|
|
661
|
+
source="$${target}.example"; \
|
|
662
|
+
case "$$name" in \
|
|
663
|
+
*.dist.*) shipped="$$name"; tracked="";; \
|
|
664
|
+
*.mjs|*.php) shipped=""; tracked="";; \
|
|
665
|
+
?*.?*) shipped=""; tracked="$${name%.*}.dist.$${name##*.}";; \
|
|
666
|
+
*) shipped=""; tracked="$${name}.dist";; \
|
|
667
|
+
esac; \
|
|
668
|
+
for directory in $(_TEMPLATE_DIRS); do \
|
|
669
|
+
[ -f "$$source" ] && break; \
|
|
670
|
+
for candidate in "$$shipped" "$$name.example" "$$tracked"; do \
|
|
671
|
+
[ -n "$$candidate" ] || continue; \
|
|
672
|
+
source="$$directory/$$candidate"; \
|
|
673
|
+
[ -f "$$source" ] && break; \
|
|
674
|
+
done; \
|
|
675
|
+
done; \
|
|
676
|
+
case "$$target" in */*) $(MKDIR) -p "$${target%/*}";; esac; \
|
|
677
|
+
if [ -f "$$source" ]; then \
|
|
678
|
+
$(CP) "$$source" "$$target" || exit $(BRNSHKR_CONFIG_ERROR_CODE); \
|
|
679
|
+
else \
|
|
680
|
+
extended="$${target%/*}/$${name%.*}.dist.$${name##*.}"; \
|
|
681
|
+
case "$$name" in \
|
|
682
|
+
*.mjs) $(PRINTF) '%s\n' "export { default } from './$${extended##*/}';" > "$$target" \
|
|
683
|
+
|| exit $(BRNSHKR_CONFIG_ERROR_CODE);; \
|
|
684
|
+
*.php) $(PRINTF) '%s\n' '<?php' '' 'declare(strict_types=1);' '' '/**' ' * @internal' ' */' \
|
|
685
|
+
"\$$config = include __DIR__ . '/$${extended##*/}';" '' 'return $$config;' > "$$target" \
|
|
686
|
+
|| exit $(BRNSHKR_CONFIG_ERROR_CODE);; \
|
|
687
|
+
*) extended=""; continue;; \
|
|
688
|
+
esac; \
|
|
689
|
+
fi; \
|
|
690
|
+
case "$$target" in $(CURDIR)/*.php) $(call _name_internal_tag,$${target#$(CURDIR)/});; esac; \
|
|
691
|
+
$(call log,Created `%s`.,$(COLOR_SUCCESS),"./$${target#$(CURDIR)/}"); \
|
|
692
|
+
if [ -n "$$extended" ]; then \
|
|
693
|
+
$(call log,Built on `%s` — keep that include.,$(COLOR_NOTICE),"./$${extended#$(CURDIR)/}"); \
|
|
694
|
+
extended=""; \
|
|
695
|
+
fi; \
|
|
696
|
+
done
|
|
697
|
+
$(DEBUG_PREFIX)for entry in $(foreach PLACED_CONFIG,$(call _selected_configs,$(_PLACED_CONFIGS)),'$(PLACED_CONFIG)'); do \
|
|
698
|
+
target="$${entry%%|*}"; \
|
|
699
|
+
source="$${entry#*|}"; \
|
|
700
|
+
[ -f "$$source" ] || continue; \
|
|
701
|
+
if [ -f "$$target" ]; then \
|
|
702
|
+
$(if $(_IS_FORCED_CONFIG),:,$(call log,`%s` already exists.,$(COLOR_NOTICE),"./$${target#$(CURDIR)/}")); \
|
|
703
|
+
$(if $(_IS_FORCED_CONFIG),:,$(if $(_NAMED_CONFIGS),$(call confirm,Overwrite it?),false)) || continue; \
|
|
704
|
+
fi; \
|
|
705
|
+
case "$$target" in */*) $(MKDIR) -p "$${target%/*}";; esac; \
|
|
706
|
+
$(CP) "$$source" "$$target" || exit $(BRNSHKR_CONFIG_ERROR_CODE); \
|
|
707
|
+
$(call log,Created `%s`.,$(COLOR_SUCCESS),"./$${target#$(CURDIR)/}"); \
|
|
708
|
+
done
|
|
709
|
+
$(DEBUG_PREFIX)$(if $(wildcard $(CURDIR)/composer.json),[ ! -f $(WORKDIR)/.gitattributes ] || { \
|
|
710
|
+
updated_path=$$($(PHP) -r "$$_GITATTRIBUTES_PROGRAM" $(WORKDIR)/.gitattributes $(WORKDIR)/composer.json '$(strip $(ARCHIVE_EXTRA_PATHS))') || exit $(BRNSHKR_CONFIG_ERROR_CODE); \
|
|
711
|
+
[ -z "$$updated_path" ] || $(call log,Updated `%s`.,$(COLOR_SUCCESS),'$(call _named_path,$(WORKDIR)/.gitattributes)'); \
|
|
712
|
+
},:)
|
|
713
|
+
$(DEBUG_PREFIX)for entry in $(foreach LINKED_CONFIG,$(call _selected_configs,$(_LINKED_CONFIGS)),'$(LINKED_CONFIG)'); do \
|
|
714
|
+
link="$${entry%%|*}"; \
|
|
715
|
+
source="$${entry#*|}"; \
|
|
716
|
+
relative="$${source#$(CURDIR)/}"; \
|
|
717
|
+
if [ -e "$$link" ]; then \
|
|
718
|
+
$(if $(_IS_FORCED_CONFIG),:,$(call log,`%s` already exists.,$(COLOR_NOTICE),"./$${link#$(CURDIR)/}")); \
|
|
719
|
+
$(if $(_IS_FORCED_CONFIG),:,$(if $(_NAMED_CONFIGS),$(call confirm,Overwrite it?),false)) || continue; \
|
|
720
|
+
fi; \
|
|
721
|
+
[ -f "$$source" ] || continue; \
|
|
722
|
+
if $(LN) -sf "$$relative" "$$link" 2>/dev/null; then \
|
|
723
|
+
$(call log,Linked `%s` to `%s`.,$(COLOR_SUCCESS),"./$${link#$(CURDIR)/}" "./$$relative"); \
|
|
724
|
+
else \
|
|
725
|
+
$(PRINTF) '%s\n' '{' " \"extends\": \"./$$relative\"" '}' > "$$link" \
|
|
726
|
+
|| exit $(BRNSHKR_CONFIG_ERROR_CODE); \
|
|
727
|
+
$(call log,Created `%s`.,$(COLOR_SUCCESS),"./$${link#$(CURDIR)/}"); \
|
|
728
|
+
fi; \
|
|
729
|
+
done
|
|
730
|
+
|
|
731
|
+
CACHE_DIR ?= $(CURDIR)/.cache#v #~~ directory the tools keep their caches in
|
|
732
|
+
_CACHES = $(if $(CACHE_DIR),$(notdir $(wildcard $(CACHE_DIR)/*)))
|
|
733
|
+
|
|
734
|
+
$(call _target,cc): #~~ removes the caches named, or all of them when given none #v
|
|
735
|
+
$(TRACE_PREFIX)$(call _require_known_value,cache)
|
|
736
|
+
$(DEBUG_PREFIX)if [ -z "$(_CACHES)" ]; then \
|
|
737
|
+
$(call log,No caches to remove.,$(COLOR_NOTICE)); \
|
|
738
|
+
elif [ -n "$(strip $(ARGS))" ]; then \
|
|
739
|
+
for name in $(sort $(ARGS)); do \
|
|
740
|
+
$(RM) -rf "$(CACHE_DIR)/$$name" || exit $(BRNSHKR_CONFIG_ERROR_CODE); \
|
|
741
|
+
$(call log,Removed `%s/%s`.,$(COLOR_SUCCESS),'$(call _named_path,$(CACHE_DIR))' "$$name"); \
|
|
742
|
+
done; \
|
|
743
|
+
else \
|
|
744
|
+
$(call log,This removes every cache in `%s`.,$(COLOR_WARNING),'$(call _named_path,$(CACHE_DIR))'); \
|
|
745
|
+
$(call log,Name %s to remove only some.,$(COLOR_NOTICE),'$(call _join_as_quoted_list,$(_CACHES),disjunction)',no-autolink); \
|
|
746
|
+
if $(call confirm,Continue?); then \
|
|
747
|
+
$(RM) -rf "$(CACHE_DIR)" || exit $(BRNSHKR_CONFIG_ERROR_CODE); \
|
|
748
|
+
$(call log,Removed `%s`.,$(COLOR_SUCCESS),'$(call _named_path,$(CACHE_DIR))'); \
|
|
749
|
+
else \
|
|
750
|
+
$(call log,Nothing removed.,$(COLOR_NOTICE)); \
|
|
751
|
+
fi; \
|
|
752
|
+
fi
|
|
753
|
+
|
|
754
|
+
_GIT_CLEAN_BASE_FLAGS = $(if $(filter --all -a,$(_ARGS)),,-e /.local -e .env*.local)
|
|
755
|
+
_GIT_CLEAN_DRY_FLAGS = -xdffn $(_GIT_CLEAN_BASE_FLAGS)
|
|
756
|
+
_GIT_CLEAN_FORCE_FLAGS = -xdff $(_GIT_CLEAN_BASE_FLAGS)
|
|
757
|
+
|
|
758
|
+
$(call _target,fresh): #~~ removes every untracked file and runs `startup`, `--all` the private ones too #vv
|
|
759
|
+
$(TRACE_PREFIX)$(GIT) rev-parse --is-inside-work-tree >/dev/null 2>&1 || { \
|
|
760
|
+
$(call log,`fresh` needs a git checkout.,$(COLOR_ERROR)) >&2; \
|
|
761
|
+
exit $(BRNSHKR_CONFIG_ERROR_CODE); \
|
|
762
|
+
}
|
|
763
|
+
$(DEBUG_PREFIX)LC_ALL=C $(GIT) clean $(_GIT_CLEAN_DRY_FLAGS) | while read -r path; do \
|
|
764
|
+
$(call log,Would remove `./%s`.,$(COLOR_NOTICE),"$${path#Would remove }"); \
|
|
765
|
+
done
|
|
766
|
+
$(DEBUG_PREFIX)$(if $(_IS_JUST_PRINT)$(call _is_dry_run,$(_ARGS)),:,$(if $(filter --force -f,$(_ARGS)),:,$(call confirm,Remove them and reinstall?) || { \
|
|
767
|
+
$(call log,Nothing removed.,$(COLOR_NOTICE)); \
|
|
768
|
+
exit 0; \
|
|
769
|
+
}); \
|
|
770
|
+
$(GIT) clean $(_GIT_CLEAN_FORCE_FLAGS))
|
|
771
|
+
$(DEBUG_PREFIX)$(if $(_IS_JUST_PRINT)$(call _is_dry_run,$(_ARGS)),:,[ -n "$$($(GIT) clean $(_GIT_CLEAN_DRY_FLAGS))" ] || { \
|
|
772
|
+
{ [ -f $(_BRNSHKR_CONFIG_MAKEFILE) ] \
|
|
773
|
+
|| { $(if $(_COMPOSER_INSTALL),$(_COMPOSER_INSTALL) &&) $(if $(_BUN_INSTALL),$(_BUN_INSTALL) &&) is_installing=_IS_INSTALLING=1; }; } \
|
|
774
|
+
&& $(MAKE) $(_VERB_FLAGS) --no-keep-going $$is_installing $(call _target,startup); \
|
|
775
|
+
})
|
|
776
|
+
|
|
777
|
+
$(call _target,fresh-dry-run): #~~ lists what `fresh` would remove #vv
|
|
778
|
+
$(DEBUG_PREFIX)$(MAKE) $(_MAKE_FLAGS) $(call _target,fresh) -- --dry-run $(ARGS)
|
|
779
|
+
|
|
780
|
+
#---vvv semver
|
|
781
|
+
|
|
782
|
+
#---- constants
|
|
783
|
+
|
|
784
|
+
SEMVER_NUMBER ?= (0|[1-9][0-9]*)#~~ a numeric identifier, which carries no leading zero
|
|
785
|
+
SEMVER_PRERELEASE_PART ?= (0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)#~~ one prerelease identifier, which carries no leading zero when it is numeric
|
|
786
|
+
SEMVER_PRERELEASE ?= $(SEMVER_PRERELEASE_PART)(\.$(SEMVER_PRERELEASE_PART))*#~~ the prerelease grammar semver itself permits
|
|
787
|
+
SEMVER_PRERELEASE_STRICT ?= (alpha|beta|rc)(\.(0|[1-9][0-9]*))?#~~ the narrower one: what npm and Composer can both express, spelled npm's way
|
|
788
|
+
SEMVER_BUILD ?= [0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*#~~ build metadata, which neither registry accepts in a published version
|
|
789
|
+
SEMVER_REGEX ?= $(SEMVER_NUMBER)\.$(SEMVER_NUMBER)\.$(SEMVER_NUMBER)(-$(SEMVER_PRERELEASE))?(\+$(SEMVER_BUILD))?#~~ the semantic version grammar, as a POSIX extended regular expression
|
|
790
|
+
|
|
791
|
+
#---v dotenv
|
|
792
|
+
|
|
793
|
+
DOTENV ?= 1#~~ base path of the environment files, `1` for `.env`, or `0` to load none
|
|
794
|
+
DOTENV_ENV_KEY ?= APP_ENV#~~ variable naming the current environment
|
|
795
|
+
DOTENV_DEFAULT_ENV ?= dev#~~ environment assumed when the variable is unset
|
|
796
|
+
DOTENV_TEST_ENVS ?= test#~~ environments that skip the `.local` file, as a test run must be reproducible
|
|
797
|
+
_DOTENV_BASE := $(if $(filter-out $(_FALSE),$(DOTENV)),$(CURDIR)/$(or $(filter-out $(_TRUE),$(DOTENV)),.env))
|
|
798
|
+
_DOTENV_SEPARATOR := @@brnshkr-dotenv-newline@@
|
|
799
|
+
_DOTENV_STAGES := $(filter-out local %.local %.example,$(patsubst .env.%,%,$(notdir $(wildcard $(CURDIR)/.env.*))))
|
|
800
|
+
|
|
801
|
+
_DOTENV_RESET = $(if $(_DOTENV_FILE_KEYS),env $(foreach KEY,$(sort $(_DOTENV_FILE_KEYS)),-u $(KEY)) )
|
|
802
|
+
|
|
803
|
+
define _DOTENV_AWK
|
|
804
|
+
function fail(message) { gsub(/\$$/, "$$$$", message); printf "$$(error [%s] %s)\n", _PREFIX, message; exit 1 }
|
|
805
|
+
function unquote(value, quote) {
|
|
806
|
+
quote = substr(value, 1, 1)
|
|
807
|
+
if ((quote == "\"" || quote == SQ) && length(value) > 1 && substr(value, length(value), 1) == quote) return substr(value, 2, length(value) - 2)
|
|
808
|
+
return value
|
|
809
|
+
}
|
|
810
|
+
function interpolate(value, out, name, start, stop) {
|
|
811
|
+
out = ""
|
|
812
|
+
while ((start = index(value, "$${")) > 0) {
|
|
813
|
+
stop = index(substr(value, start), "}")
|
|
814
|
+
if (stop == 0) break
|
|
815
|
+
name = substr(value, start + 2, stop - 3)
|
|
816
|
+
out = out substr(value, 1, start - 1) ((name in ENVIRON) ? ENVIRON[name] : (name in values) ? values[name] : "")
|
|
817
|
+
value = substr(value, start + stop)
|
|
818
|
+
}
|
|
819
|
+
return out value
|
|
820
|
+
}
|
|
821
|
+
function load(path, line, key, value, quote, continued) {
|
|
822
|
+
if ((getline line < path) < 0) return 0
|
|
823
|
+
do {
|
|
824
|
+
sub(/\r$$/, "", line)
|
|
825
|
+
if (line ~ /^[ \t]*($$|#)/) continue
|
|
826
|
+
sub(/^[ \t]*export[ \t]+/, "", line)
|
|
827
|
+
if (line !~ /^[A-Za-z_][A-Za-z0-9_]*[ \t]*=/) continue
|
|
828
|
+
key = line
|
|
829
|
+
sub(/[ \t]*=.*$$/, "", key)
|
|
830
|
+
value = substr(line, index(line, "=") + 1)
|
|
831
|
+
sub(/^[ \t]+/, "", value)
|
|
832
|
+
quote = substr(value, 1, 1)
|
|
833
|
+
if (quote == "\"" || quote == SQ) {
|
|
834
|
+
while (!(length(value) > 1 && substr(value, length(value), 1) == quote)) {
|
|
835
|
+
if ((getline continued < path) <= 0) fail("unterminated quote in `" path "`")
|
|
836
|
+
sub(/\r$$/, "", continued)
|
|
837
|
+
value = value "\n" continued
|
|
838
|
+
}
|
|
839
|
+
} else {
|
|
840
|
+
sub(/[ \t]+#.*$$/, "", value)
|
|
841
|
+
sub(/[ \t]+$$/, "", value)
|
|
842
|
+
}
|
|
843
|
+
if (quote != SQ && value ~ /\$$\(/) fail("`$$(...)` in `" path "` is not supported, use `$${VAR}`")
|
|
844
|
+
value = unquote(value)
|
|
845
|
+
if (quote != SQ) value = interpolate(value)
|
|
846
|
+
if (!(key in values)) order[++count] = key
|
|
847
|
+
values[key] = value
|
|
848
|
+
} while ((getline line < path) > 0)
|
|
849
|
+
close(path)
|
|
850
|
+
return 1
|
|
851
|
+
}
|
|
852
|
+
function environment(fallback, value) { value = _ENV_VALUE != "" ? _ENV_VALUE : (_ENV_KEY in values) ? values[_ENV_KEY] : ""; return value != "" ? value : fallback }
|
|
853
|
+
BEGIN {
|
|
854
|
+
SQ = sprintf("%c", 39)
|
|
855
|
+
if (!load(_BASE)) load(_BASE ".dist")
|
|
856
|
+
env = environment(_DEFAULT_ENV)
|
|
857
|
+
split(_TEST_ENVS, tests, /[ \t]+/)
|
|
858
|
+
for (i in tests) if (tests[i] == env) is_test = 1
|
|
859
|
+
if (!is_test) { load(_BASE ".local"); env = environment(env) }
|
|
860
|
+
if (env != "local") { load(_BASE "." env); load(_BASE "." env ".local") }
|
|
861
|
+
if (!(_ENV_KEY in values)) order[++count] = _ENV_KEY
|
|
862
|
+
values[_ENV_KEY] = env
|
|
863
|
+
for (i = 1; i <= count; i += 1) {
|
|
864
|
+
key = order[i]
|
|
865
|
+
value = values[key]
|
|
866
|
+
gsub(/\$$/, "$$$$", value)
|
|
867
|
+
gsub(/#/, "\\#", value)
|
|
868
|
+
if (value ~ /\n/) {
|
|
869
|
+
gsub(/\n/, _SEPARATOR, value)
|
|
870
|
+
printf "define %s ?=%s%s%sendef%s", key, _SEPARATOR, value, _SEPARATOR, _SEPARATOR
|
|
871
|
+
} else {
|
|
872
|
+
printf "%s ?= %s%s", key, value, _SEPARATOR
|
|
873
|
+
}
|
|
874
|
+
printf "export %s%s", key, _SEPARATOR
|
|
875
|
+
if (!(key in ENVIRON)) printf "_DOTENV_FILE_KEYS += %s%s", key, _SEPARATOR
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
endef
|
|
879
|
+
|
|
880
|
+
_DOTENV_ASSIGNMENTS := $(if $(wildcard $(_DOTENV_BASE) $(_DOTENV_BASE).dist),$(shell \
|
|
881
|
+
$(AWK) \
|
|
882
|
+
-v _BASE='$(_DOTENV_BASE)' \
|
|
883
|
+
-v _ENV_KEY='$(DOTENV_ENV_KEY)' \
|
|
884
|
+
-v _ENV_VALUE='$($(DOTENV_ENV_KEY))' \
|
|
885
|
+
-v _DEFAULT_ENV='$(DOTENV_DEFAULT_ENV)' \
|
|
886
|
+
-v _TEST_ENVS='$(DOTENV_TEST_ENVS)' \
|
|
887
|
+
-v _PREFIX='$(_LABEL)' \
|
|
888
|
+
-v _SEPARATOR='$(_DOTENV_SEPARATOR)' \
|
|
889
|
+
'$(_DOTENV_AWK)' \
|
|
890
|
+
))
|
|
891
|
+
|
|
892
|
+
$(eval $(subst $(_DOTENV_SEPARATOR),$(_NEWLINE),$(_DOTENV_ASSIGNMENTS)))
|
|
893
|
+
|
|
894
|
+
#**
|
|
895
|
+
#* Defines `<stage>-<target>`, which runs that target with the environment key set to that stage.
|
|
896
|
+
#*
|
|
897
|
+
#* parameters:
|
|
898
|
+
#* stage: string
|
|
899
|
+
#*
|
|
900
|
+
#* returns: void
|
|
901
|
+
#*
|
|
902
|
+
define _stage_rule
|
|
903
|
+
$1-%: _SUGGESTED_FOR = $$*
|
|
904
|
+
$1-%:
|
|
905
|
+
$$(TRACE_PREFIX)$$(if $$(call _goal_of,$$*),:,$$(call log,Unknown command `%s`.,$$(COLOR_ERROR),'$$@'); \
|
|
906
|
+
$$(call _suggest,$1-))
|
|
907
|
+
$$(DEBUG_PREFIX)$$(if $$(call _goal_of,$$*),$$(_DOTENV_RESET)$$(MAKE) $$(_MAKE_FLAGS) $$(_INHERITED_FLAGS) $(DOTENV_ENV_KEY)=$1 $$* \
|
|
908
|
+
$$(if $$(_UNKNOWN_ARGS),-- $$(_UNKNOWN_ARGS)),:)
|
|
909
|
+
endef
|
|
910
|
+
|
|
911
|
+
$(foreach STAGE,$(_DOTENV_STAGES),$(eval $(call _stage_rule,$(STAGE))))
|
|
912
|
+
|
|
913
|
+
#---v helpers
|
|
914
|
+
|
|
915
|
+
#**
|
|
916
|
+
#* The value it is given when this project tracks a file the extensions name, or when git cannot say.
|
|
917
|
+
#*
|
|
918
|
+
#* parameters:
|
|
919
|
+
#* value: list<string>
|
|
920
|
+
#* extensions: list<string>
|
|
921
|
+
#*
|
|
922
|
+
#* returns: list<string>
|
|
923
|
+
#*
|
|
924
|
+
define _when_tracked
|
|
925
|
+
$(if $(_TRACKED_EXTENSIONS),$(if $(filter $(addprefix .,$2),$(_TRACKED_EXTENSIONS)),$1),$1)
|
|
926
|
+
endef
|
|
927
|
+
|
|
928
|
+
#**
|
|
929
|
+
#* The value it is given when the path is there, so a list only claims what this project has.
|
|
930
|
+
#*
|
|
931
|
+
#* parameters:
|
|
932
|
+
#* value: list<string>
|
|
933
|
+
#* path: string
|
|
934
|
+
#*
|
|
935
|
+
#* returns: list<string>
|
|
936
|
+
#*
|
|
937
|
+
define _when_installed
|
|
938
|
+
$(if $(wildcard $(CURDIR)/$2),$1)
|
|
939
|
+
endef
|
|
940
|
+
|
|
941
|
+
ifneq ($(wildcard $(CURDIR)/composer.json),) # php
|
|
942
|
+
|
|
943
|
+
#--- php
|
|
944
|
+
|
|
945
|
+
PHP_EXTENSIONS ?= php#vv #~~ extensions the PHP tools read
|
|
946
|
+
|
|
947
|
+
#--- composer
|
|
948
|
+
|
|
949
|
+
COMPOSER ?= $(_RUN) composer#v #~~ command that runs `composer`
|
|
950
|
+
unexport COMPOSER # NOTICE: Composer reads this as the manifest to open
|
|
951
|
+
COMPOSER_FLAGS ?= $(if $(_IS_CI),--no-interaction --no-progress) $(if $(_IS_DEBUG),-vvv)#v #~~ additional flags passed to `composer`
|
|
952
|
+
_COMPOSER_PACKAGE_DIR := $(CURDIR)/.local/$(if $(VENDOR),$(patsubst @%,%,$(VENDOR))/)$(PACKAGE)
|
|
953
|
+
_COMPOSER_INSTALL = $(COMPOSER) install $(COMPOSER_FLAGS)
|
|
954
|
+
|
|
955
|
+
_PACK_NAMES += composer-pack
|
|
956
|
+
|
|
957
|
+
$(call _target,composer): #~~ runs `composer`
|
|
958
|
+
$(DEBUG_PREFIX)$(COMPOSER) $(COMPOSER_FLAGS) $(ARGS)
|
|
959
|
+
|
|
960
|
+
$(call _target,composer-list): #~~ lists the files the composer package would ship #vv
|
|
961
|
+
$(DEBUG_PREFIX)$(COMPOSER) archive --quiet --format=tar --dir=$(WORKDIR) --file=package \
|
|
962
|
+
&& $(TAR) -tf package.tar \
|
|
963
|
+
&& $(RM) -f package.tar
|
|
964
|
+
|
|
965
|
+
$(call _target,composer-pack): #~~ packs the composer package into `./.local/<VENDOR>/<PACKAGE>` #vvv
|
|
966
|
+
$(DEBUG_PREFIX)$(RM) -rf $(_COMPOSER_PACKAGE_DIR) \
|
|
967
|
+
&& $(MKDIR) -p $(_COMPOSER_PACKAGE_DIR) \
|
|
968
|
+
&& $(COMPOSER) archive --quiet --format=tar --dir=$(WORKDIR) --file=package \
|
|
969
|
+
&& $(TAR) -xf package.tar -C $(_COMPOSER_PACKAGE_DIR) \
|
|
970
|
+
&& $(RM) -f package.tar
|
|
971
|
+
|
|
972
|
+
$(call _target,composer-print): #~~ prints the resolved `composer` configuration #vv
|
|
973
|
+
$(DEBUG_PREFIX)$(COMPOSER) config --list $(COMPOSER_FLAGS) $(ARGS)
|
|
974
|
+
|
|
975
|
+
_$(_TARGET_PREFIX)composer-install:
|
|
976
|
+
$(DEBUG_PREFIX)$(_COMPOSER_INSTALL)
|
|
977
|
+
|
|
978
|
+
#--- phpunit
|
|
979
|
+
|
|
980
|
+
ifneq ($(wildcard $(CURDIR)/vendor/bin/pest $(CURDIR)/vendor/bin/phpunit),) # phpunit
|
|
981
|
+
|
|
982
|
+
PEST ?= $(_RUN) $(_PEST_BINARY)#vvv #~~ command that runs `pest`
|
|
983
|
+
PHP_UNIT ?= $(if $(wildcard $(CURDIR)/vendor/bin/pest),$(PEST),$(_RUN) $(WORKDIR)/vendor/bin/phpunit)#v #~~ command that runs the tests, which is Pest when it is installed
|
|
984
|
+
PHP_UNIT_CONFIG ?= $(call _config,$(WORKDIR)/conf/phpunit.xml $(WORKDIR)/conf/phpunit.dist.xml,php)#v #~~ path to the test runner config
|
|
985
|
+
PHP_UNIT_FLAGS ?= $(if $(_IS_DEBUG),--debug) $(if $(_IS_NO_ANSI),--colors=never)#v #~~ additional flags passed to the test runner
|
|
986
|
+
PHP_UNIT_SNAPSHOT_FLAGS ?= $(if $(_IS_PEST),--update-snapshots) --do-not-fail-on-incomplete#v #~~ flags that make the runner rewrite its snapshots
|
|
987
|
+
PHP_UNIT_COVERAGE_DIR ?= $(WORKDIR)/.cache/phpunit.cache/coverage#v #~~ directory the HTML coverage report is written to
|
|
988
|
+
PHP_UNIT_COVERAGE_FILE ?= $(PHP_UNIT_COVERAGE_DIR)/coverage.txt#vv #~~ text report the minimum is read from, where the runner has no minimum of its own
|
|
989
|
+
PHP_UNIT_MIN_COVERAGE ?= 100#v #~~ percentage every metric must reach, `0` to collect coverage without a floor
|
|
990
|
+
PHP_UNIT_MIN_COVERAGE_CLASSES ?= $(PHP_UNIT_MIN_COVERAGE)#vv #~~ percentage the class metric must reach, which Pest cannot read
|
|
991
|
+
PHP_UNIT_MIN_COVERAGE_METHODS ?= $(PHP_UNIT_MIN_COVERAGE)#vv #~~ percentage the method metric must reach, which Pest cannot read
|
|
992
|
+
PHP_UNIT_MIN_COVERAGE_LINES ?= $(PHP_UNIT_MIN_COVERAGE)#vv #~~ percentage the line metric must reach
|
|
993
|
+
PHP_UNIT_COVERAGE_FLAGS ?= $(if $(_IS_PEST),--coverage $(if $(_PHP_UNIT_FLOOR),--min=$(_PHP_UNIT_FLOOR)),$(if $(_PHP_UNIT_FLOORS),--coverage-text=$(PHP_UNIT_COVERAGE_FILE))) --coverage-html $(PHP_UNIT_COVERAGE_DIR)#v #~~ additional flags passed when collecting coverage
|
|
994
|
+
|
|
995
|
+
_PEST_BINARY := $(WORKDIR)/vendor/bin/pest
|
|
996
|
+
_IS_PEST = $(findstring pest,$(PHP_UNIT))
|
|
997
|
+
_PHP_UNIT_UPDATE = $(if $(wildcard $(CURDIR)/vendor/bin/update-snapshots),$(subst /vendor/bin/phpunit,/vendor/bin/update-snapshots,$(PHP_UNIT)),$(PHP_UNIT))
|
|
998
|
+
_PHP_UNIT_FLOOR = $(filter-out 0,$(strip $(PHP_UNIT_MIN_COVERAGE_LINES)))
|
|
999
|
+
|
|
1000
|
+
_PHP_UNIT_FLOORS = Classes=$(strip $(PHP_UNIT_MIN_COVERAGE_CLASSES)) \
|
|
1001
|
+
Methods=$(strip $(PHP_UNIT_MIN_COVERAGE_METHODS)) \
|
|
1002
|
+
Lines=$(strip $(PHP_UNIT_MIN_COVERAGE_LINES))
|
|
1003
|
+
|
|
1004
|
+
_TEST_NAMES += $(if $(wildcard $(CURDIR)/tests),$(call _when_tracked,phpunit,$(PHP_EXTENSIONS)))
|
|
1005
|
+
_COVERAGE_NAMES += $(if $(wildcard $(CURDIR)/tests),$(call _when_tracked,phpunit,$(PHP_EXTENSIONS)))
|
|
1006
|
+
_LAYERED_CONFIGS += $(if $(wildcard $(CURDIR)/tests),$(call _when_tracked,$(call _layered,$(PHP_UNIT_CONFIG),xml),$(PHP_EXTENSIONS)))
|
|
1007
|
+
|
|
1008
|
+
_$(_TARGET_PREFIX)assert-pest:
|
|
1009
|
+
$(TRACE_PREFIX)test -f '$(call _host_path,$(_PEST_BINARY))' || { \
|
|
1010
|
+
$(call log,`pest` is not installed.,$(COLOR_ERROR)) >&2; \
|
|
1011
|
+
$(call log,Require `pestphp/pest` or run `make $(call _target,phpunit)` to use the runner this project has.,$(COLOR_NOTICE)) >&2; \
|
|
1012
|
+
exit $(BRNSHKR_CONFIG_ERROR_CODE); \
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
$(call _target,pest): _$(_TARGET_PREFIX)assert-pest #~~ runs the PHP tests through `pest` in particular #vvv
|
|
1016
|
+
$(DEBUG_PREFIX)$(MAKE) $(_MAKE_FLAGS) $(call _target,phpunit) PHP_UNIT='$(PEST)' -- $(ARGS)
|
|
1017
|
+
|
|
1018
|
+
$(call _target,pest-coverage): _$(_TARGET_PREFIX)assert-pest #~~ runs them through `pest` and reports its coverage #vvv
|
|
1019
|
+
$(DEBUG_PREFIX)$(MAKE) $(_MAKE_FLAGS) $(call _target,phpunit-coverage) PHP_UNIT='$(PEST)' -- $(ARGS)
|
|
1020
|
+
|
|
1021
|
+
$(call _target,pest-debug): _$(_TARGET_PREFIX)assert-pest #~~ runs them through `pest` with debugging information #vvv
|
|
1022
|
+
$(DEBUG_PREFIX)$(MAKE) $(_MAKE_FLAGS) $(call _target,phpunit-debug) PHP_UNIT='$(PEST)' -- $(ARGS)
|
|
1023
|
+
|
|
1024
|
+
$(call _target,pest-list): _$(_TARGET_PREFIX)assert-pest #~~ lists the tests `pest` collects #vvv
|
|
1025
|
+
$(DEBUG_PREFIX)$(MAKE) $(_MAKE_FLAGS) $(call _target,phpunit-list) PHP_UNIT='$(PEST)' -- $(ARGS)
|
|
1026
|
+
|
|
1027
|
+
$(call _target,pest-update): _$(_TARGET_PREFIX)assert-pest #~~ runs them through `pest` and updates their snapshots #vvv
|
|
1028
|
+
$(DEBUG_PREFIX)$(MAKE) $(_MAKE_FLAGS) $(call _target,phpunit-update) PHP_UNIT='$(PEST)' -- $(ARGS)
|
|
1029
|
+
|
|
1030
|
+
$(call _target,phpunit): #~~ runs the PHP tests
|
|
1031
|
+
$(TRACE_PREFIX)$(call _require_file,$(PHP_UNIT_CONFIG),PHP_UNIT_CONFIG)
|
|
1032
|
+
$(DEBUG_PREFIX)$(call _spinner,$(PHP_UNIT) --configuration $(PHP_UNIT_CONFIG) $(PHP_UNIT_FLAGS) $(ARGS))
|
|
1033
|
+
|
|
1034
|
+
$(call _target,phpunit-coverage): #~~ runs the PHP tests and reports how much of the source they reach #v
|
|
1035
|
+
$(TRACE_PREFIX)$(call _require_file,$(PHP_UNIT_CONFIG),PHP_UNIT_CONFIG)
|
|
1036
|
+
$(DEBUG_PREFIX)$(call _spinner,$(PHP_UNIT) --configuration $(PHP_UNIT_CONFIG) $(PHP_UNIT_FLAGS) $(PHP_UNIT_COVERAGE_FLAGS) $(ARGS))
|
|
1037
|
+
$(TRACE_PREFIX)$(if $(or $(_IS_PEST),$(if $(_PHP_UNIT_FLOORS),,1)),:,$(foreach FLOOR,$(_PHP_UNIT_FLOORS),$(call _require_coverage,$(call _host_path,$(PHP_UNIT_COVERAGE_FILE)),$(firstword $(subst =, ,$(FLOOR))),$(lastword $(subst =, ,$(FLOOR))),PHP_UNIT_COVERAGE_FILE)$(_NEWLINE)))
|
|
1038
|
+
|
|
1039
|
+
$(call _target,phpunit-debug): #~~ runs the PHP tests with debugging information instead of progress #vvv
|
|
1040
|
+
$(DEBUG_PREFIX)$(MAKE) $(_MAKE_FLAGS) $(call _target,phpunit) -- --debug $(ARGS)
|
|
1041
|
+
|
|
1042
|
+
$(call _target,phpunit-list): #~~ lists the tests the runner collects #vv
|
|
1043
|
+
$(TRACE_PREFIX)$(call _require_file,$(PHP_UNIT_CONFIG),PHP_UNIT_CONFIG)
|
|
1044
|
+
$(DEBUG_PREFIX)$(PHP_UNIT) --configuration $(PHP_UNIT_CONFIG) $(ARGS) --list-tests
|
|
1045
|
+
|
|
1046
|
+
$(call _target,phpunit-update): #~~ runs the PHP tests and updates their snapshots #v
|
|
1047
|
+
$(TRACE_PREFIX)$(call _require_file,$(PHP_UNIT_CONFIG),PHP_UNIT_CONFIG)
|
|
1048
|
+
$(DEBUG_PREFIX)$(_PHP_UNIT_UPDATE) --configuration $(PHP_UNIT_CONFIG) $(PHP_UNIT_FLAGS) $(PHP_UNIT_SNAPSHOT_FLAGS) $(ARGS)
|
|
1049
|
+
|
|
1050
|
+
endif # /phpunit
|
|
1051
|
+
|
|
1052
|
+
#--- phpstan
|
|
1053
|
+
|
|
1054
|
+
ifneq ($(wildcard $(CURDIR)/vendor/bin/phpstan),) # phpstan
|
|
1055
|
+
|
|
1056
|
+
PHP_STAN ?= $(_RUN) $(WORKDIR)/vendor/bin/phpstan#v #~~ path to `phpstan` binary
|
|
1057
|
+
PHP_STAN_CONFIG ?= $(call _config,$(WORKDIR)/conf/phpstan.php $(WORKDIR)/conf/phpstan.dist.php,php)#v #~~ path to `phpstan` config
|
|
1058
|
+
PHP_STAN_FLAGS ?= --memory-limit=-1 $(_NO_ANSI_OPTION) $(if $(_IS_DEBUG),-vvv,-vv)#v #~~ additional flags passed to `phpstan`
|
|
1059
|
+
|
|
1060
|
+
_ANALYZER_NAMES += $(call _when_tracked,phpstan,$(PHP_EXTENSIONS))
|
|
1061
|
+
_GROUP_NAMES += $(call _when_tracked,phpstan,$(PHP_EXTENSIONS))
|
|
1062
|
+
_LAYERED_CONFIGS += $(call _when_tracked,$(call _layered,$(PHP_STAN_CONFIG),php),$(PHP_EXTENSIONS))
|
|
1063
|
+
|
|
1064
|
+
$(call _target,phpstan): #~~ runs `phpstan analyze`
|
|
1065
|
+
$(TRACE_PREFIX)$(call _require_file,$(PHP_STAN_CONFIG),PHP_STAN_CONFIG)
|
|
1066
|
+
$(DEBUG_PREFIX)$(call _spinner,$(PHP_STAN) analyze --configuration $(PHP_STAN_CONFIG) $(PHP_STAN_FLAGS) $(ARGS))
|
|
1067
|
+
|
|
1068
|
+
$(call _target,phpstan-debug): #~~ runs `phpstan analyze` in debug mode #vvv
|
|
1069
|
+
$(DEBUG_PREFIX)$(MAKE) $(_MAKE_FLAGS) $(call _target,phpstan) -- --debug $(ARGS)
|
|
1070
|
+
|
|
1071
|
+
$(call _target,phpstan-group): #~~ counts the `phpstan` findings by identifier #v
|
|
1072
|
+
$(TRACE_PREFIX)$(call _require_file,$(PHP_STAN_CONFIG),PHP_STAN_CONFIG)
|
|
1073
|
+
$(DEBUG_PREFIX)$(call _group,$(PHP_STAN) analyze --configuration $(PHP_STAN_CONFIG) $(PHP_STAN_FLAGS) --no-progress --error-format=json $(ARGS),identifier,message)
|
|
1074
|
+
|
|
1075
|
+
$(call _target,phpstan-list): #~~ lists all files that are processed by `phpstan` #vv
|
|
1076
|
+
$(DEBUG_PREFIX)$(call _capture,$(MAKE) $(_MAKE_FLAGS) $(call _target,phpstan-raw) -- --debug 2>&1) \
|
|
1077
|
+
| $(AWK) 'match($$0, /.+\.php$$/) { print substr($$0, RSTART, RLENGTH) }'
|
|
1078
|
+
|
|
1079
|
+
$(call _target,phpstan-print): #~~ prints the resolved `phpstan` configuration #vv
|
|
1080
|
+
$(TRACE_PREFIX)$(call _require_file,$(PHP_STAN_CONFIG),PHP_STAN_CONFIG)
|
|
1081
|
+
$(DEBUG_PREFIX)$(PHP_STAN) dump-parameters --configuration $(PHP_STAN_CONFIG) $(_NO_ANSI_OPTION) $(ARGS)
|
|
1082
|
+
|
|
1083
|
+
$(call _target,phpstan-raw): #~~ runs `phpstan analyze` with raw output #vvv
|
|
1084
|
+
$(TRACE_PREFIX)$(call _require_file,$(PHP_STAN_CONFIG),PHP_STAN_CONFIG)
|
|
1085
|
+
$(DEBUG_PREFIX)$(PHP_STAN) analyze --configuration $(PHP_STAN_CONFIG) $(PHP_STAN_FLAGS) --error-format=raw $(ARGS)
|
|
1086
|
+
|
|
1087
|
+
endif # /phpstan
|
|
1088
|
+
|
|
1089
|
+
#--- php-cs-fixer
|
|
1090
|
+
|
|
1091
|
+
ifneq ($(wildcard $(CURDIR)/vendor/bin/php-cs-fixer),) # php-cs-fixer
|
|
1092
|
+
|
|
1093
|
+
PHP_CS_FIXER ?= $(_RUN) $(WORKDIR)/vendor/bin/php-cs-fixer#v #~~ path to `php-cs-fixer` binary
|
|
1094
|
+
PHP_CS_FIXER_CONFIG ?= $(call _config,$(WORKDIR)/conf/php-cs-fixer.php $(WORKDIR)/conf/php-cs-fixer.dist.php,php)#v #~~ path to `php-cs-fixer` config
|
|
1095
|
+
PHP_CS_FIXER_FLAGS ?= --show-progress=dots $(_NO_ANSI_OPTION) $(if $(_IS_DEBUG),-vvv,-v)#v #~~ additional flags passed to `php-cs-fixer`
|
|
1096
|
+
|
|
1097
|
+
_FIXER_NAMES += $(call _when_tracked,php-cs-fixer,$(PHP_EXTENSIONS))
|
|
1098
|
+
_GROUP_NAMES += $(call _when_tracked,php-cs-fixer,$(PHP_EXTENSIONS))
|
|
1099
|
+
_LAYERED_CONFIGS += $(call _when_tracked,$(call _layered,$(PHP_CS_FIXER_CONFIG),php),$(PHP_EXTENSIONS))
|
|
1100
|
+
|
|
1101
|
+
$(call _target,php-cs-fixer): #~~ runs `php-cs-fixer fix`
|
|
1102
|
+
$(TRACE_PREFIX)$(call _require_file,$(PHP_CS_FIXER_CONFIG),PHP_CS_FIXER_CONFIG)
|
|
1103
|
+
$(DEBUG_PREFIX)$(call _spinner,$(PHP_CS_FIXER) fix --config $(PHP_CS_FIXER_CONFIG) $(PHP_CS_FIXER_FLAGS) $(ARGS))
|
|
1104
|
+
|
|
1105
|
+
$(call _target,php-cs-fixer-dry-run): #~~ runs `php-cs-fixer fix` in dry run mode #v
|
|
1106
|
+
$(DEBUG_PREFIX)$(MAKE) $(_MAKE_FLAGS) $(call _target,php-cs-fixer) -- --dry-run $(ARGS)
|
|
1107
|
+
|
|
1108
|
+
$(call _target,php-cs-fixer-group): #~~ counts the `php-cs-fixer` findings by fixer #v
|
|
1109
|
+
$(TRACE_PREFIX)$(call _require_file,$(PHP_CS_FIXER_CONFIG),PHP_CS_FIXER_CONFIG)
|
|
1110
|
+
$(DEBUG_PREFIX)$(call _group,$(PHP_CS_FIXER) fix --config $(PHP_CS_FIXER_CONFIG) $(PHP_CS_FIXER_FLAGS) $(ARGS) --dry-run --format=json,appliedFixers)
|
|
1111
|
+
|
|
1112
|
+
$(call _target,php-cs-fixer-list): #~~ lists all files that are processed by `php-cs-fixer` #vv
|
|
1113
|
+
$(TRACE_PREFIX)$(call _require_file,$(PHP_CS_FIXER_CONFIG),PHP_CS_FIXER_CONFIG)
|
|
1114
|
+
$(DEBUG_PREFIX)$(call _capture,$(PHP_CS_FIXER) list-files --config $(PHP_CS_FIXER_CONFIG) $(_NO_ANSI_OPTION) 2>&1) \
|
|
1115
|
+
| $(AWK) '/^'\''\.\/.*\.php'\''$$/ { sub(/^'\''\.\//, ""); sub(/'\''$$/, ""); print }'
|
|
1116
|
+
|
|
1117
|
+
$(call _target,php-cs-fixer-print): #~~ prints the `php-cs-fixer` rules in use #vv
|
|
1118
|
+
$(TRACE_PREFIX)$(call _require_file,$(PHP_CS_FIXER_CONFIG),PHP_CS_FIXER_CONFIG)
|
|
1119
|
+
$(DEBUG_PREFIX)$(PHP_CS_FIXER) describe --config $(PHP_CS_FIXER_CONFIG) $(_NO_ANSI_OPTION) -n @ $(ARGS)
|
|
1120
|
+
|
|
1121
|
+
endif # /php-cs-fixer
|
|
1122
|
+
|
|
1123
|
+
#--- rector
|
|
1124
|
+
|
|
1125
|
+
ifneq ($(wildcard $(CURDIR)/vendor/bin/rector),) # rector
|
|
1126
|
+
|
|
1127
|
+
RECTOR ?= $(_RUN) $(WORKDIR)/vendor/bin/rector#v #~~ path to `rector` binary
|
|
1128
|
+
RECTOR_CONFIG ?= $(call _config,$(WORKDIR)/conf/rector.php $(WORKDIR)/conf/rector.dist.php,php)#v #~~ path to `rector` config
|
|
1129
|
+
RECTOR_FLAGS ?= --memory-limit=-1 $(_NO_ANSI_OPTION)#v #~~ additional flags passed to `rector`
|
|
1130
|
+
|
|
1131
|
+
_FIXER_NAMES += $(call _when_tracked,rector,$(PHP_EXTENSIONS))
|
|
1132
|
+
_GROUP_NAMES += $(call _when_tracked,rector,$(PHP_EXTENSIONS))
|
|
1133
|
+
_LAYERED_CONFIGS += $(call _when_tracked,$(call _layered,$(RECTOR_CONFIG),php),$(PHP_EXTENSIONS))
|
|
1134
|
+
|
|
1135
|
+
$(call _target,rector): #~~ runs `rector process`
|
|
1136
|
+
$(TRACE_PREFIX)$(call _require_file,$(RECTOR_CONFIG),RECTOR_CONFIG)
|
|
1137
|
+
$(DEBUG_PREFIX)$(call _spinner,$(RECTOR) process --config $(RECTOR_CONFIG) $(RECTOR_FLAGS) $(ARGS))
|
|
1138
|
+
|
|
1139
|
+
$(call _target,rector-debug): #~~ runs `rector process` in debug mode #vvv
|
|
1140
|
+
$(DEBUG_PREFIX)$(MAKE) $(_MAKE_FLAGS) $(call _target,rector) -- --debug $(ARGS)
|
|
1141
|
+
|
|
1142
|
+
$(call _target,rector-dry-run): #~~ runs `rector process` in dry run mode #v
|
|
1143
|
+
$(DEBUG_PREFIX)$(MAKE) $(_MAKE_FLAGS) $(call _target,rector) -- --dry-run $(ARGS)
|
|
1144
|
+
|
|
1145
|
+
$(call _target,rector-group): #~~ counts the `rector` findings by rule #v
|
|
1146
|
+
$(TRACE_PREFIX)$(call _require_file,$(RECTOR_CONFIG),RECTOR_CONFIG)
|
|
1147
|
+
$(DEBUG_PREFIX)$(call _group,$(RECTOR) process --config $(RECTOR_CONFIG) $(RECTOR_FLAGS) $(ARGS) --dry-run --output-format=json,applied_rectors)
|
|
1148
|
+
|
|
1149
|
+
$(call _target,rector-list): #~~ lists all files that are processed by `rector` #vv
|
|
1150
|
+
$(TRACE_PREFIX)$(call _require_file,$(RECTOR_CONFIG),RECTOR_CONFIG)
|
|
1151
|
+
$(DEBUG_PREFIX)$(call _capture,$(RECTOR) process --config $(RECTOR_CONFIG) --debug --dry-run) | $(AWK) '/\[file\]/ { sub(".*\\[file\\] $(WORKDIR)/", ""); if (/\.php$$/) print }'
|
|
1152
|
+
|
|
1153
|
+
$(call _target,rector-print): #~~ prints the `rector` rules in use #vv
|
|
1154
|
+
$(TRACE_PREFIX)$(call _require_file,$(RECTOR_CONFIG),RECTOR_CONFIG)
|
|
1155
|
+
$(DEBUG_PREFIX)$(RECTOR) list-rules --config $(RECTOR_CONFIG) $(_NO_ANSI_OPTION) $(ARGS)
|
|
1156
|
+
|
|
1157
|
+
endif # /rector
|
|
1158
|
+
|
|
1159
|
+
#--- twig-cs-fixer
|
|
1160
|
+
|
|
1161
|
+
ifneq ($(wildcard $(CURDIR)/vendor/bin/twig-cs-fixer),) # twig-cs-fixer
|
|
1162
|
+
|
|
1163
|
+
TWIG_CS_FIXER ?= $(_RUN) $(WORKDIR)/vendor/bin/twig-cs-fixer#v #~~ path to `twig-cs-fixer` binary
|
|
1164
|
+
TWIG_CS_FIXER_CONFIG ?= $(call _config,$(WORKDIR)/conf/twig-cs-fixer.php $(WORKDIR)/conf/twig-cs-fixer.dist.php,php)#v #~~ path to `twig-cs-fixer` config
|
|
1165
|
+
TWIG_CS_FIXER_FLAGS ?= -v $(_NO_ANSI_OPTION)#v #~~ additional flags passed to `twig-cs-fixer`
|
|
1166
|
+
TWIG_CS_FIXER_EXTENSIONS ?= twig#vv #~~ extensions `twig-cs-fixer` reads
|
|
1167
|
+
|
|
1168
|
+
_FIXER_NAMES += $(call _when_tracked,twig-cs-fixer,$(TWIG_CS_FIXER_EXTENSIONS))
|
|
1169
|
+
_GROUP_NAMES += $(call _when_tracked,twig-cs-fixer,$(TWIG_CS_FIXER_EXTENSIONS))
|
|
1170
|
+
_LAYERED_CONFIGS += $(call _when_tracked,$(call _layered,$(TWIG_CS_FIXER_CONFIG),php),$(TWIG_CS_FIXER_EXTENSIONS))
|
|
1171
|
+
|
|
1172
|
+
$(call _target,twig-cs-fixer): #~~ runs `twig-cs-fixer fix`, or lints only when `--dry-run` is passed
|
|
1173
|
+
$(TRACE_PREFIX)$(call _require_file,$(TWIG_CS_FIXER_CONFIG),TWIG_CS_FIXER_CONFIG)
|
|
1174
|
+
$(DEBUG_PREFIX)$(call _spinner,$(TWIG_CS_FIXER) lint $(if $(call _is_dry_run,$(_ARGS)),,--fix) --config $(TWIG_CS_FIXER_CONFIG) $(TWIG_CS_FIXER_FLAGS) $(_FIXER_ARGS))
|
|
1175
|
+
|
|
1176
|
+
$(call _target,twig-cs-fixer-debug): #~~ runs `twig-cs-fixer` in debug mode #vvv
|
|
1177
|
+
$(DEBUG_PREFIX)$(MAKE) $(_MAKE_FLAGS) $(call _target,twig-cs-fixer) -- --debug $(ARGS)
|
|
1178
|
+
|
|
1179
|
+
$(call _target,twig-cs-fixer-dry-run): #~~ runs `twig-cs-fixer` in dry run mode #v
|
|
1180
|
+
$(DEBUG_PREFIX)$(MAKE) $(_MAKE_FLAGS) $(call _target,twig-cs-fixer) -- --dry-run $(ARGS)
|
|
1181
|
+
|
|
1182
|
+
$(call _target,twig-cs-fixer-group): #~~ counts the `twig-cs-fixer` findings by rule #v
|
|
1183
|
+
$(TRACE_PREFIX)$(call _require_file,$(TWIG_CS_FIXER_CONFIG),TWIG_CS_FIXER_CONFIG)
|
|
1184
|
+
$(DEBUG_PREFIX)$(call _group,$(TWIG_CS_FIXER) lint --config $(TWIG_CS_FIXER_CONFIG) --report gitlab $(ARGS),check_name,description)
|
|
1185
|
+
|
|
1186
|
+
endif # /twig-cs-fixer
|
|
1187
|
+
|
|
1188
|
+
endif # /php
|
|
1189
|
+
|
|
1190
|
+
ifneq ($(wildcard $(CURDIR)/package.json),) # js
|
|
1191
|
+
|
|
1192
|
+
#--- bun
|
|
1193
|
+
|
|
1194
|
+
BUN ?= $(_RUN) bun#v #~~ command that runs `bun`
|
|
1195
|
+
BUN_FLAGS ?= --bun#v #~~ flags passed to `bun` before the command
|
|
1196
|
+
|
|
1197
|
+
_BUN_PACKAGE_DIR := $(CURDIR)/.local/$(if $(VENDOR),$(VENDOR)/)$(PACKAGE)
|
|
1198
|
+
_BUN_INSTALL = $(BUN) install
|
|
1199
|
+
|
|
1200
|
+
_PACK_NAMES += bun-pack
|
|
1201
|
+
|
|
1202
|
+
$(call _target,bun): #~~ runs `bun`
|
|
1203
|
+
$(DEBUG_PREFIX)$(BUN) $(ARGS)
|
|
1204
|
+
|
|
1205
|
+
$(call _target,bun-list): #~~ lists the files the bun package would ship #vv
|
|
1206
|
+
$(DEBUG_PREFIX)$(call _capture,NO_COLOR=1 $(BUN) pm pack --dry-run) | $(AWK) '/^packed /{ print $$3 }'
|
|
1207
|
+
|
|
1208
|
+
$(call _target,bun-pack): #~~ packs the bun package into `./.local/<VENDOR>/<PACKAGE>` #vvv
|
|
1209
|
+
$(DEBUG_PREFIX)$(RM) -rf $(_BUN_PACKAGE_DIR) \
|
|
1210
|
+
&& $(MKDIR) -p $(_BUN_PACKAGE_DIR) \
|
|
1211
|
+
&& $(BUN) pm pack --quiet --filename package.tgz >/dev/null \
|
|
1212
|
+
&& $(TAR) -xzf package.tgz -C $(_BUN_PACKAGE_DIR) --strip-components 1 \
|
|
1213
|
+
&& $(RM) -f package.tgz
|
|
1214
|
+
|
|
1215
|
+
ifneq ($(wildcard $(CURDIR)/node_modules/.bin/node-modules-inspector),) # bun-inspect
|
|
1216
|
+
|
|
1217
|
+
NODE_MODULES_INSPECTOR ?= $(BUN) $(BUN_FLAGS) x node-modules-inspector#v #~~ command that runs `node-modules-inspector`
|
|
1218
|
+
NODE_MODULES_INSPECTOR_FLAGS ?= #v #~~ additional flags passed to `node-modules-inspector`
|
|
1219
|
+
|
|
1220
|
+
$(call _target,bun-inspect): #~~ runs `node-modules-inspector` #v
|
|
1221
|
+
$(DEBUG_PREFIX)$(NODE_MODULES_INSPECTOR) $(ARGS) $(NODE_MODULES_INSPECTOR_FLAGS)
|
|
1222
|
+
|
|
1223
|
+
endif # /bun-inspect
|
|
1224
|
+
|
|
1225
|
+
_$(_TARGET_PREFIX)bun-install:
|
|
1226
|
+
$(DEBUG_PREFIX)$(_BUN_INSTALL)
|
|
1227
|
+
|
|
1228
|
+
#--- commitlint
|
|
1229
|
+
|
|
1230
|
+
ifneq ($(wildcard $(CURDIR)/node_modules/.bin/commitlint),) # commitlint
|
|
1231
|
+
|
|
1232
|
+
COMMITLINT ?= $(BUN) $(BUN_FLAGS) x commitlint#v #~~ command that runs `commitlint`
|
|
1233
|
+
COMMITLINT_CONFIG ?= $(call _config,$(WORKDIR)/conf/commitlint.mjs $(WORKDIR)/conf/commitlint.dist.mjs,js)#v #~~ path to `commitlint` config
|
|
1234
|
+
COMMITLINT_SOURCE ?= $(if $(wildcard $(CURDIR)/.git/COMMIT_EDITMSG),--edit,--last)#vv #~~ which message `commitlint` reads
|
|
1235
|
+
COMMITLINT_FLAGS ?= $(if $(_IS_DEBUG),--verbose) $(if $(_IS_NO_ANSI),--color false)#v #~~ additional flags passed to `commitlint`
|
|
1236
|
+
|
|
1237
|
+
GIT_HOOKS += commit-msg=$(call _target,commitlint)
|
|
1238
|
+
|
|
1239
|
+
_ANALYZER_NAMES += commitlint
|
|
1240
|
+
_LAYERED_CONFIGS += $(call _layered,$(COMMITLINT_CONFIG),mjs)
|
|
1241
|
+
|
|
1242
|
+
$(call _target,commitlint): _$(_TARGET_PREFIX)install-missing-hooks #~~ lints a commit message
|
|
1243
|
+
$(TRACE_PREFIX)$(call _require_file,$(COMMITLINT_CONFIG),COMMITLINT_CONFIG)
|
|
1244
|
+
$(DEBUG_PREFIX)$(call _spinner,$(COMMITLINT) --config $(COMMITLINT_CONFIG) $(COMMITLINT_FLAGS) $(if $(ARGS),$(ARGS),$(COMMITLINT_SOURCE)))
|
|
1245
|
+
|
|
1246
|
+
$(call _target,commitlint-print): #~~ prints the resolved `commitlint` configuration #vv
|
|
1247
|
+
$(TRACE_PREFIX)$(call _require_file,$(COMMITLINT_CONFIG),COMMITLINT_CONFIG)
|
|
1248
|
+
$(DEBUG_PREFIX)$(COMMITLINT) --config $(COMMITLINT_CONFIG) $(COMMITLINT_FLAGS) --print-config $(ARGS)
|
|
1249
|
+
|
|
1250
|
+
endif # /commitlint
|
|
1251
|
+
|
|
1252
|
+
#--- eslint
|
|
1253
|
+
|
|
1254
|
+
ifneq ($(wildcard $(CURDIR)/node_modules/.bin/eslint),) # eslint
|
|
1255
|
+
|
|
1256
|
+
ESLINT ?= $(BUN) $(BUN_FLAGS) x eslint#v #~~ command that runs `eslint`
|
|
1257
|
+
ESLINT_CONFIG ?= $(call _config,$(WORKDIR)/conf/eslint.ts $(WORKDIR)/conf/eslint.mjs $(WORKDIR)/conf/eslint.dist.mjs,js)#v #~~ path to `eslint` config
|
|
1258
|
+
ESLINT_CACHE ?= $(WORKDIR)/.cache/eslint.cache.json#v #~~ path to the `eslint` cache
|
|
1259
|
+
ESLINT_FLAGS ?= --max-warnings 0 $(if $(_IS_DEBUG),--debug) $(if $(_IS_NO_ANSI),--no-color)#v #~~ additional flags passed to `eslint`
|
|
1260
|
+
unexport ESLINT_FLAGS # NOTICE: ESLint reads this as its feature flags
|
|
1261
|
+
|
|
1262
|
+
_ESLINT_OUTPUT := $(WORKDIR)/.cache/eslint.output
|
|
1263
|
+
|
|
1264
|
+
_ESLINT_EXTENSIONS := js cjs mjs jsx \
|
|
1265
|
+
$(call _when_installed,ts cts mts tsx,node_modules/typescript-eslint) \
|
|
1266
|
+
$(call _when_installed,svelte,node_modules/eslint-plugin-svelte) \
|
|
1267
|
+
$(call _when_installed,json,node_modules/@eslint/json) \
|
|
1268
|
+
$(call _when_installed,jsonc json5,node_modules/eslint-plugin-jsonc) \
|
|
1269
|
+
$(call _when_installed,yml yaml,node_modules/eslint-plugin-yml) \
|
|
1270
|
+
$(call _when_installed,toml,node_modules/eslint-plugin-toml) \
|
|
1271
|
+
$(call _when_installed,css,node_modules/@eslint/css) \
|
|
1272
|
+
$(call _when_installed,md,node_modules/@eslint/markdown)
|
|
1273
|
+
|
|
1274
|
+
ESLINT_EXTENSIONS ?= $(_ESLINT_EXTENSIONS)#vv #~~ extensions `eslint` lints, which follows the modules its config enables
|
|
1275
|
+
|
|
1276
|
+
_FIXER_NAMES += $(call _when_tracked,eslint,$(ESLINT_EXTENSIONS))
|
|
1277
|
+
_GROUP_NAMES += $(call _when_tracked,eslint,$(ESLINT_EXTENSIONS))
|
|
1278
|
+
_LAYERED_CONFIGS += $(call _when_tracked,$(call _layered,$(ESLINT_CONFIG),mjs),$(ESLINT_EXTENSIONS))
|
|
1279
|
+
|
|
1280
|
+
# NOTICE: ESLint writes through `--output-file` since Bun truncates its piped output
|
|
1281
|
+
$(call _target,eslint): #~~ runs `eslint`, and writes its fixes unless `--dry-run` is passed
|
|
1282
|
+
$(TRACE_PREFIX)$(call _require_file,$(ESLINT_CONFIG),ESLINT_CONFIG)
|
|
1283
|
+
$(DEBUG_PREFIX)$(call _spinner,$(RM) -f '$(call _host_path,$(_ESLINT_OUTPUT))'; \
|
|
1284
|
+
$(ESLINT) --config $(ESLINT_CONFIG) --cache --cache-location $(ESLINT_CACHE) $(ESLINT_FLAGS) --output-file $(_ESLINT_OUTPUT) $(_FIXER_ARGS) $(if $(call _is_dry_run,$(_ARGS)),,--fix); \
|
|
1285
|
+
status=$$?; \
|
|
1286
|
+
[ ! -f '$(call _host_path,$(_ESLINT_OUTPUT))' ] || $(CAT) '$(call _host_path,$(_ESLINT_OUTPUT))'; \
|
|
1287
|
+
$(RM) -f '$(call _host_path,$(_ESLINT_OUTPUT))'; \
|
|
1288
|
+
exit $$status)
|
|
1289
|
+
|
|
1290
|
+
$(call _target,eslint-dry-run): #~~ runs `eslint` without writing fixes #v
|
|
1291
|
+
$(DEBUG_PREFIX)$(MAKE) $(_MAKE_FLAGS) $(call _target,eslint) -- --dry-run $(ARGS)
|
|
1292
|
+
|
|
1293
|
+
$(call _target,eslint-group): #~~ counts the `eslint` findings by rule #v
|
|
1294
|
+
$(TRACE_PREFIX)$(call _require_file,$(ESLINT_CONFIG),ESLINT_CONFIG)
|
|
1295
|
+
$(DEBUG_PREFIX)$(call _group,$(ESLINT) --config $(ESLINT_CONFIG) --cache --cache-location $(ESLINT_CACHE) $(ESLINT_FLAGS) --format json --output-file /dev/stdout $(ARGS),ruleId,message)
|
|
1296
|
+
|
|
1297
|
+
$(call _target,eslint-print): #~~ prints the resolved `eslint` config for a file #vv
|
|
1298
|
+
$(TRACE_PREFIX)$(call _require_file,$(ESLINT_CONFIG),ESLINT_CONFIG)
|
|
1299
|
+
$(DEBUG_PREFIX)$(ESLINT) --config $(ESLINT_CONFIG) --print-config $(ARGS)
|
|
1300
|
+
|
|
1301
|
+
ifneq ($(wildcard $(CURDIR)/node_modules/.bin/eslint-config-inspector),) # eslint-inspect
|
|
1302
|
+
|
|
1303
|
+
# NOTICE: runs on node since bun has no `module.registerHooks` (https://github.com/oven-sh/bun/issues/27369)
|
|
1304
|
+
ESLINT_CONFIG_INSPECTOR ?= $(BUN) x eslint-config-inspector#v #~~ command that runs `eslint-config-inspector`
|
|
1305
|
+
ESLINT_CONFIG_INSPECTOR_FLAGS ?= #v #~~ additional flags passed to `eslint-config-inspector`
|
|
1306
|
+
|
|
1307
|
+
$(call _target,eslint-inspect): #~~ runs `eslint-config-inspector` #v
|
|
1308
|
+
$(TRACE_PREFIX)$(call _require_file,$(ESLINT_CONFIG),ESLINT_CONFIG)
|
|
1309
|
+
$(DEBUG_PREFIX)$(ESLINT_CONFIG_INSPECTOR) --config $(ESLINT_CONFIG) $(ARGS) $(ESLINT_CONFIG_INSPECTOR_FLAGS)
|
|
1310
|
+
|
|
1311
|
+
$(call _target,eslint-inspect-stats): #~~ runs `eslint-config-inspector` and times every rule with a full lint on startup #vv
|
|
1312
|
+
$(DEBUG_PREFIX)$(MAKE) $(_MAKE_FLAGS) $(call _target,eslint-inspect) -- $(ARGS) --stats
|
|
1313
|
+
|
|
1314
|
+
endif # /eslint-inspect
|
|
1315
|
+
|
|
1316
|
+
endif # /eslint
|
|
1317
|
+
|
|
1318
|
+
#--- markdownlint
|
|
1319
|
+
|
|
1320
|
+
ifneq ($(wildcard $(CURDIR)/node_modules/.bin/markdownlint-cli2),) # markdownlint
|
|
1321
|
+
|
|
1322
|
+
MARKDOWNLINT ?= $(BUN) $(BUN_FLAGS) x markdownlint-cli2#v #~~ command that runs `markdownlint-cli2`
|
|
1323
|
+
MARKDOWNLINT_CONFIG ?= $(call _config,$(WORKDIR)/conf/markdownlint.mjs $(WORKDIR)/conf/markdownlint.dist.mjs,js)#v #~~ path to `markdownlint` config
|
|
1324
|
+
MARKDOWNLINT_GLOB ?= $(call _brace_glob,$(MARKDOWNLINT_EXTENSIONS))#vv #~~ files `markdownlint` looks at
|
|
1325
|
+
MARKDOWNLINT_FLAGS ?= $(if $(_IS_NO_ANSI),--no-color)#v #~~ additional flags passed to `markdownlint-cli2`
|
|
1326
|
+
MARKDOWNLINT_EXTENSIONS ?= md markdown#vv #~~ extensions `markdownlint` reads
|
|
1327
|
+
|
|
1328
|
+
_MARKDOWNLINT_VALUE_FLAGS := --config \
|
|
1329
|
+
--configPointer
|
|
1330
|
+
|
|
1331
|
+
_FIXER_NAMES += $(call _when_tracked,markdownlint,$(MARKDOWNLINT_EXTENSIONS))
|
|
1332
|
+
_GROUP_NAMES += $(call _when_tracked,markdownlint,$(MARKDOWNLINT_EXTENSIONS))
|
|
1333
|
+
_LAYERED_CONFIGS += $(call _when_tracked,$(call _layered,$(MARKDOWNLINT_CONFIG),mjs),$(MARKDOWNLINT_EXTENSIONS))
|
|
1334
|
+
|
|
1335
|
+
$(call _target,markdownlint): #~~ runs `markdownlint-cli2`, and writes its fixes unless `--dry-run` is passed
|
|
1336
|
+
$(TRACE_PREFIX)$(call _require_file,$(MARKDOWNLINT_CONFIG),MARKDOWNLINT_CONFIG)
|
|
1337
|
+
$(DEBUG_PREFIX)$(call _spinner,$(MARKDOWNLINT) --config $(MARKDOWNLINT_CONFIG) $(MARKDOWNLINT_FLAGS) \
|
|
1338
|
+
$(if $(call _is_dry_run,$(_ARGS)),,--fix) \
|
|
1339
|
+
$(if $(call _has_positional_arg,$(call _without_dry_run,$(_ARGS)),$(_MARKDOWNLINT_VALUE_FLAGS)), \
|
|
1340
|
+
$(_FIXER_ARGS), \
|
|
1341
|
+
$(_FIXER_ARGS) '$(MARKDOWNLINT_GLOB)'))
|
|
1342
|
+
|
|
1343
|
+
$(call _target,markdownlint-dry-run): #~~ runs `markdownlint-cli2` without writing fixes #v
|
|
1344
|
+
$(DEBUG_PREFIX)$(MAKE) $(_MAKE_FLAGS) $(call _target,markdownlint) -- --dry-run $(ARGS)
|
|
1345
|
+
|
|
1346
|
+
$(call _target,markdownlint-group): #~~ counts the `markdownlint` findings by rule #v
|
|
1347
|
+
$(TRACE_PREFIX)$(call _require_file,$(MARKDOWNLINT_CONFIG),MARKDOWNLINT_CONFIG)
|
|
1348
|
+
$(DEBUG_PREFIX)$(call _group,$(MARKDOWNLINT) --config $(MARKDOWNLINT_CONFIG) $(MARKDOWNLINT_FLAGS) $(if $(call _has_positional_arg,$(ARGS),$(_MARKDOWNLINT_VALUE_FLAGS)),$(ARGS),$(ARGS) '$(MARKDOWNLINT_GLOB)'))
|
|
1349
|
+
|
|
1350
|
+
endif # /markdownlint
|
|
1351
|
+
|
|
1352
|
+
#--- stylelint
|
|
1353
|
+
|
|
1354
|
+
ifneq ($(wildcard $(CURDIR)/node_modules/.bin/stylelint),) # stylelint
|
|
1355
|
+
|
|
1356
|
+
STYLELINT ?= $(BUN) $(BUN_FLAGS) x stylelint#v #~~ command that runs `stylelint`
|
|
1357
|
+
STYLELINT_CONFIG ?= $(call _config,$(WORKDIR)/conf/stylelint.mjs $(WORKDIR)/conf/stylelint.dist.mjs,js)#v #~~ path to `stylelint` config
|
|
1358
|
+
STYLELINT_GLOB ?= $(call _brace_glob,$(STYLELINT_EXTENSIONS))#vv #~~ files `stylelint` looks at
|
|
1359
|
+
STYLELINT_FLAGS ?= --allow-empty-input --max-warnings 0 $(if $(_IS_NO_ANSI),--no-color)#v #~~ additional flags passed to `stylelint`
|
|
1360
|
+
|
|
1361
|
+
_STYLELINT_VALUE_FLAGS := --cache-location \
|
|
1362
|
+
--cache-strategy \
|
|
1363
|
+
--config-basedir \
|
|
1364
|
+
--config \
|
|
1365
|
+
--custom-formatter \
|
|
1366
|
+
--custom-syntax \
|
|
1367
|
+
--formatter \
|
|
1368
|
+
--globby-options \
|
|
1369
|
+
--go \
|
|
1370
|
+
--ignore-path \
|
|
1371
|
+
--ignore-pattern \
|
|
1372
|
+
--ip \
|
|
1373
|
+
--max-warnings \
|
|
1374
|
+
--mw \
|
|
1375
|
+
--output-file \
|
|
1376
|
+
--stdin-filename \
|
|
1377
|
+
--suppress-location \
|
|
1378
|
+
-c \
|
|
1379
|
+
-f \
|
|
1380
|
+
-i \
|
|
1381
|
+
-o
|
|
1382
|
+
|
|
1383
|
+
_STYLELINT_EXTENSIONS := css \
|
|
1384
|
+
$(call _when_installed,less,node_modules/stylelint-config-standard-less) \
|
|
1385
|
+
$(call _when_installed,scss,node_modules/stylelint-config-standard-scss) \
|
|
1386
|
+
$(call _when_installed,ejs html svelte svg vue astro xml,node_modules/stylelint-config-html)
|
|
1387
|
+
|
|
1388
|
+
STYLELINT_EXTENSIONS ?= $(_STYLELINT_EXTENSIONS)#vv #~~ extensions `stylelint` can parse, which follows the syntaxes its config pulls in
|
|
1389
|
+
|
|
1390
|
+
_FIXER_NAMES += $(call _when_tracked,stylelint,$(STYLELINT_EXTENSIONS))
|
|
1391
|
+
_GROUP_NAMES += $(call _when_tracked,stylelint,$(STYLELINT_EXTENSIONS))
|
|
1392
|
+
_LAYERED_CONFIGS += $(call _when_tracked,$(call _layered,$(STYLELINT_CONFIG),mjs),$(STYLELINT_EXTENSIONS))
|
|
1393
|
+
|
|
1394
|
+
$(call _target,stylelint): #~~ runs `stylelint`, and writes its fixes unless `--dry-run` is passed
|
|
1395
|
+
$(TRACE_PREFIX)$(call _require_file,$(STYLELINT_CONFIG),STYLELINT_CONFIG)
|
|
1396
|
+
$(DEBUG_PREFIX)$(call _spinner,$(STYLELINT) --config $(STYLELINT_CONFIG) $(STYLELINT_FLAGS) \
|
|
1397
|
+
$(if $(call _is_dry_run,$(_ARGS)),,--fix) \
|
|
1398
|
+
$(if $(call _has_positional_arg,$(call _without_dry_run,$(_ARGS)),$(_STYLELINT_VALUE_FLAGS)), \
|
|
1399
|
+
$(_FIXER_ARGS), \
|
|
1400
|
+
$(_FIXER_ARGS) '$(STYLELINT_GLOB)'))
|
|
1401
|
+
|
|
1402
|
+
$(call _target,stylelint-dry-run): #~~ runs `stylelint` without writing fixes #v
|
|
1403
|
+
$(DEBUG_PREFIX)$(MAKE) $(_MAKE_FLAGS) $(call _target,stylelint) -- --dry-run $(ARGS)
|
|
1404
|
+
|
|
1405
|
+
$(call _target,stylelint-group): #~~ counts the `stylelint` findings by rule #v
|
|
1406
|
+
$(TRACE_PREFIX)$(call _require_file,$(STYLELINT_CONFIG),STYLELINT_CONFIG)
|
|
1407
|
+
$(DEBUG_PREFIX)$(call _group,$(STYLELINT) --config $(STYLELINT_CONFIG) $(STYLELINT_FLAGS) --formatter json $(if $(call _has_positional_arg,$(ARGS),$(_STYLELINT_VALUE_FLAGS)),$(ARGS),$(ARGS) '$(STYLELINT_GLOB)'),rule,text)
|
|
1408
|
+
|
|
1409
|
+
$(call _target,stylelint-print): #~~ prints the resolved `stylelint` config for a file #vv
|
|
1410
|
+
$(TRACE_PREFIX)$(call _require_file,$(STYLELINT_CONFIG),STYLELINT_CONFIG)
|
|
1411
|
+
$(DEBUG_PREFIX)$(STYLELINT) --config $(STYLELINT_CONFIG) --print-config $(ARGS)
|
|
1412
|
+
|
|
1413
|
+
endif # /stylelint
|
|
1414
|
+
|
|
1415
|
+
#--- typescript
|
|
1416
|
+
|
|
1417
|
+
ifneq ($(wildcard $(CURDIR)/node_modules/.bin/tsc),) # typescript
|
|
1418
|
+
|
|
1419
|
+
TYPESCRIPT ?= $(BUN) $(BUN_FLAGS) x tsc#v #~~ command that runs `tsc`
|
|
1420
|
+
TYPESCRIPT_CONFIG ?= $(WORKDIR)/tsconfig.json#v #~~ path to the `tsc` project
|
|
1421
|
+
TYPESCRIPT_FLAGS ?= --noEmit#v #~~ additional flags passed to `tsc`
|
|
1422
|
+
TYPESCRIPT_EXTENSIONS ?= ts tsx cts mts#vv #~~ extensions `tsc` type checks
|
|
1423
|
+
|
|
1424
|
+
_TYPESCRIPT_PROJECT := $(WORKDIR)/conf/tsconfig.json
|
|
1425
|
+
|
|
1426
|
+
_ANALYZER_NAMES += $(call _when_tracked,typescript,$(TYPESCRIPT_EXTENSIONS))
|
|
1427
|
+
_GROUP_NAMES += $(call _when_tracked,typescript,$(TYPESCRIPT_EXTENSIONS))
|
|
1428
|
+
_PLAIN_CONFIGS += $(call _when_tracked,$(_TYPESCRIPT_PROJECT),$(TYPESCRIPT_EXTENSIONS))
|
|
1429
|
+
_LINKED_CONFIGS += $(call _when_tracked,$(call _host_path,$(TYPESCRIPT_CONFIG))|$(call _host_path,$(_TYPESCRIPT_PROJECT)),$(TYPESCRIPT_EXTENSIONS))
|
|
1430
|
+
|
|
1431
|
+
$(call _target,typescript): #~~ runs the `tsc` type check
|
|
1432
|
+
$(TRACE_PREFIX)$(call _require_file,$(TYPESCRIPT_CONFIG),TYPESCRIPT_CONFIG)
|
|
1433
|
+
$(DEBUG_PREFIX)$(call _spinner,$(TYPESCRIPT) --project $(TYPESCRIPT_CONFIG) $(TYPESCRIPT_FLAGS) $(ARGS))
|
|
1434
|
+
|
|
1435
|
+
$(call _target,typescript-group): #~~ counts the `tsc` errors by code #v
|
|
1436
|
+
$(TRACE_PREFIX)$(call _require_file,$(TYPESCRIPT_CONFIG),TYPESCRIPT_CONFIG)
|
|
1437
|
+
$(DEBUG_PREFIX)$(call _group,$(TYPESCRIPT) --project $(TYPESCRIPT_CONFIG) $(TYPESCRIPT_FLAGS) --pretty false $(ARGS))
|
|
1438
|
+
|
|
1439
|
+
$(call _target,typescript-list): #~~ lists the files the `tsc` project includes #vv
|
|
1440
|
+
$(TRACE_PREFIX)$(call _require_file,$(TYPESCRIPT_CONFIG),TYPESCRIPT_CONFIG)
|
|
1441
|
+
$(DEBUG_PREFIX)$(TYPESCRIPT) --project $(TYPESCRIPT_CONFIG) --listFilesOnly $(ARGS)
|
|
1442
|
+
|
|
1443
|
+
$(call _target,typescript-print): #~~ prints the resolved `tsc` project #vv
|
|
1444
|
+
$(TRACE_PREFIX)$(call _require_file,$(TYPESCRIPT_CONFIG),TYPESCRIPT_CONFIG)
|
|
1445
|
+
$(DEBUG_PREFIX)$(TYPESCRIPT) --project $(TYPESCRIPT_CONFIG) --showConfig $(ARGS)
|
|
1446
|
+
|
|
1447
|
+
endif # /typescript
|
|
1448
|
+
|
|
1449
|
+
#--- vitest
|
|
1450
|
+
|
|
1451
|
+
ifneq ($(wildcard $(CURDIR)/node_modules/.bin/vitest),) # vitest
|
|
1452
|
+
|
|
1453
|
+
VITEST ?= $(BUN) $(BUN_FLAGS) x vitest#v #~~ command that runs `vitest`
|
|
1454
|
+
unexport VITEST # NOTICE: Vitest sets this to mark its own runs
|
|
1455
|
+
VITEST_CONFIG ?= $(call _config,$(WORKDIR)/conf/vitest.ts $(WORKDIR)/conf/vitest.mjs $(WORKDIR)/conf/vitest.dist.mjs,js)#v #~~ path to `vitest` config
|
|
1456
|
+
VITEST_EXTENSIONS ?= ts tsx js jsx mts cts mjs cjs#vv #~~ extensions `vitest` runs tests from
|
|
1457
|
+
VITEST_FLAGS ?= --run --passWithNoTests $(if $(_IS_DEBUG),--reporter=verbose)#v #~~ additional flags passed to `vitest`
|
|
1458
|
+
|
|
1459
|
+
VITEST_COVERAGE_DIR ?= $(WORKDIR)/.cache/vitest.cache/coverage#v #~~ directory the HTML coverage report is written to
|
|
1460
|
+
VITEST_MIN_COVERAGE ?= 100#v #~~ percentage every metric must reach, `0` to collect coverage without a floor
|
|
1461
|
+
VITEST_MIN_COVERAGE_BRANCHES ?= $(VITEST_MIN_COVERAGE)#vv #~~ percentage the branch metric must reach
|
|
1462
|
+
VITEST_MIN_COVERAGE_FUNCTIONS ?= $(VITEST_MIN_COVERAGE)#vv #~~ percentage the function metric must reach
|
|
1463
|
+
VITEST_MIN_COVERAGE_LINES ?= $(VITEST_MIN_COVERAGE)#vv #~~ percentage the line metric must reach
|
|
1464
|
+
VITEST_MIN_COVERAGE_STATEMENTS ?= $(VITEST_MIN_COVERAGE)#vv #~~ percentage the statement metric must reach
|
|
1465
|
+
|
|
1466
|
+
VITEST_COVERAGE_FLAGS?= --coverage \
|
|
1467
|
+
--coverage.reporter=text \
|
|
1468
|
+
--coverage.reporter=html \
|
|
1469
|
+
--coverage.reportsDirectory=$(VITEST_COVERAGE_DIR) \
|
|
1470
|
+
--coverage.thresholds.branches=$(VITEST_MIN_COVERAGE_BRANCHES) \
|
|
1471
|
+
--coverage.thresholds.functions=$(VITEST_MIN_COVERAGE_FUNCTIONS) \
|
|
1472
|
+
--coverage.thresholds.lines=$(VITEST_MIN_COVERAGE_LINES) \
|
|
1473
|
+
--coverage.thresholds.statements=$(VITEST_MIN_COVERAGE_STATEMENTS)#v #~~ additional flags passed when collecting coverage
|
|
1474
|
+
|
|
1475
|
+
_VITEST_COVERAGE_PROVIDER := $(firstword $(wildcard \
|
|
1476
|
+
$(CURDIR)/node_modules/@vitest/coverage-v8 \
|
|
1477
|
+
$(CURDIR)/node_modules/@vitest/coverage-istanbul \
|
|
1478
|
+
))
|
|
1479
|
+
|
|
1480
|
+
_TEST_NAMES += $(if $(wildcard $(CURDIR)/tests),$(call _when_tracked,vitest,$(VITEST_EXTENSIONS)))
|
|
1481
|
+
_COVERAGE_NAMES += $(if $(_VITEST_COVERAGE_PROVIDER),$(if $(wildcard $(CURDIR)/tests),$(call _when_tracked,vitest,$(VITEST_EXTENSIONS))))
|
|
1482
|
+
_LAYERED_CONFIGS += $(if $(wildcard $(CURDIR)/tests),$(call _when_tracked,$(call _layered,$(VITEST_CONFIG),mjs),$(VITEST_EXTENSIONS)))
|
|
1483
|
+
|
|
1484
|
+
$(call _target,vitest): #~~ runs `vitest`
|
|
1485
|
+
$(TRACE_PREFIX)$(call _require_file,$(VITEST_CONFIG),VITEST_CONFIG)
|
|
1486
|
+
$(DEBUG_PREFIX)$(call _spinner,$(VITEST) --config $(VITEST_CONFIG) $(VITEST_FLAGS) $(ARGS))
|
|
1487
|
+
|
|
1488
|
+
ifneq ($(_VITEST_COVERAGE_PROVIDER),) # vitest-coverage
|
|
1489
|
+
|
|
1490
|
+
$(call _target,vitest-coverage): #~~ runs `vitest` and reports how much of the source it reaches #v
|
|
1491
|
+
$(TRACE_PREFIX)$(call _require_file,$(VITEST_CONFIG),VITEST_CONFIG)
|
|
1492
|
+
$(DEBUG_PREFIX)$(call _spinner,$(VITEST) --config $(VITEST_CONFIG) $(VITEST_FLAGS) $(VITEST_COVERAGE_FLAGS) $(ARGS))
|
|
1493
|
+
|
|
1494
|
+
endif # /vitest-coverage
|
|
1495
|
+
|
|
1496
|
+
$(call _target,vitest-list): #~~ lists the tests `vitest` collects #vv
|
|
1497
|
+
$(TRACE_PREFIX)$(call _require_file,$(VITEST_CONFIG),VITEST_CONFIG)
|
|
1498
|
+
$(DEBUG_PREFIX)$(VITEST) list --config $(VITEST_CONFIG) $(ARGS)
|
|
1499
|
+
|
|
1500
|
+
$(call _target,vitest-update): #~~ runs `vitest` and updates its snapshots #v
|
|
1501
|
+
$(TRACE_PREFIX)$(call _require_file,$(VITEST_CONFIG),VITEST_CONFIG)
|
|
1502
|
+
$(DEBUG_PREFIX)$(call _spinner,$(VITEST) --config $(VITEST_CONFIG) $(VITEST_FLAGS) $(ARGS) --update)
|
|
1503
|
+
|
|
1504
|
+
endif # /vitest
|
|
1505
|
+
|
|
1506
|
+
endif # /js
|
|
1507
|
+
|
|
1508
|
+
#--- changelog
|
|
1509
|
+
|
|
1510
|
+
CHANGELOG_RANGE ?= #v #~~ commit range the changelog covers
|
|
1511
|
+
CHANGELOG_URL ?= #v #~~ base URL a commit hash links to
|
|
1512
|
+
CHANGELOG_DIR ?= $(CURDIR)/changelog#vv #~~ directory the release notes are written to
|
|
1513
|
+
CHANGELOG_TITLE ?= \# Changelog#vv #~~ heading a new changelog file starts with
|
|
1514
|
+
|
|
1515
|
+
CHANGELOG_SKIPPED_COMMITS ?= chore(hygiene) \
|
|
1516
|
+
chore(release)#vv #~~ `type(scope)` pairs left out of the changelog
|
|
1517
|
+
|
|
1518
|
+
CHANGELOG_SKIPPED_TYPES ?= test \
|
|
1519
|
+
style \
|
|
1520
|
+
build#vv #~~ commit types left out of the changelog
|
|
1521
|
+
|
|
1522
|
+
CHANGELOG_GROUPS ?= dependencies=📦_Dependencies \
|
|
1523
|
+
a11y=♿_Accessibility \
|
|
1524
|
+
admin=🛠️_Admin \
|
|
1525
|
+
ai=🤖_AI \
|
|
1526
|
+
api=🔌_API \
|
|
1527
|
+
assets=🖼️_Assets \
|
|
1528
|
+
auth=🔐_Auth \
|
|
1529
|
+
backend=🧱_Backend \
|
|
1530
|
+
cache=🗃️_Cache \
|
|
1531
|
+
ci=⚙️_CI \
|
|
1532
|
+
cli=⌨️_CLI \
|
|
1533
|
+
composer=📦_Composer \
|
|
1534
|
+
config=🔧_Configuration \
|
|
1535
|
+
db=🗄️_Database \
|
|
1536
|
+
docker=🐳_Docker \
|
|
1537
|
+
docs=📚_Documentation \
|
|
1538
|
+
events=📡_Events \
|
|
1539
|
+
forms=📝_Forms \
|
|
1540
|
+
frontend=🖥️_Frontend \
|
|
1541
|
+
i18n=🌍_Translations \
|
|
1542
|
+
js=☕_JS \
|
|
1543
|
+
logging=🧾_Logging \
|
|
1544
|
+
mail=✉️_Mail \
|
|
1545
|
+
make=🔨_Make \
|
|
1546
|
+
payment=💳_Payment \
|
|
1547
|
+
perf=⚡_Performance \
|
|
1548
|
+
php=🐘_PHP \
|
|
1549
|
+
queue=📬_Queue \
|
|
1550
|
+
readme=📚_Documentation \
|
|
1551
|
+
release=🔖_Release \
|
|
1552
|
+
routing=🧭_Routing \
|
|
1553
|
+
rule=🧩_Rules \
|
|
1554
|
+
schedule=⏰_Schedule \
|
|
1555
|
+
search=🔍_Search \
|
|
1556
|
+
security=🔒_Security \
|
|
1557
|
+
seo=🔎_SEO \
|
|
1558
|
+
storage=💾_Storage \
|
|
1559
|
+
templates=🖌️_Templates \
|
|
1560
|
+
test=🧪_Tests \
|
|
1561
|
+
twig=🌿_Twig \
|
|
1562
|
+
ui=🎨_UI \
|
|
1563
|
+
vscode=💻_Editor \
|
|
1564
|
+
general=🧰_General#vv #~~ headings per scope root, as `<root>=<heading>` pairs, `_` a space
|
|
1565
|
+
|
|
1566
|
+
CHANGELOG_NAMES ?= a11y=a11y \
|
|
1567
|
+
acl=ACL \
|
|
1568
|
+
apcu=APCu \
|
|
1569
|
+
api=API \
|
|
1570
|
+
aws=AWS \
|
|
1571
|
+
bdd=BDD \
|
|
1572
|
+
cdn=CDN \
|
|
1573
|
+
cgi=CGI \
|
|
1574
|
+
cli=CLI \
|
|
1575
|
+
cms=CMS \
|
|
1576
|
+
commitlint=commitlint \
|
|
1577
|
+
cors=CORS \
|
|
1578
|
+
csp=CSP \
|
|
1579
|
+
css=CSS \
|
|
1580
|
+
csv=CSV \
|
|
1581
|
+
db=Database \
|
|
1582
|
+
dbal=DBAL \
|
|
1583
|
+
di=DI \
|
|
1584
|
+
dns=DNS \
|
|
1585
|
+
dto=DTO \
|
|
1586
|
+
dx=DX \
|
|
1587
|
+
esbuild=esbuild \
|
|
1588
|
+
eslint=ESLint \
|
|
1589
|
+
fastcgi=FastCGI \
|
|
1590
|
+
ffi=FFI \
|
|
1591
|
+
fpm=FPM \
|
|
1592
|
+
frankenphp=FrankenPHP \
|
|
1593
|
+
gcp=GCP \
|
|
1594
|
+
github=GitHub \
|
|
1595
|
+
gitlab=GitLab \
|
|
1596
|
+
graphql=GraphQL \
|
|
1597
|
+
grpc=gRPC \
|
|
1598
|
+
gui=GUI \
|
|
1599
|
+
html=HTML \
|
|
1600
|
+
http=HTTP \
|
|
1601
|
+
i18n=i18n \
|
|
1602
|
+
iam=IAM \
|
|
1603
|
+
ide=IDE \
|
|
1604
|
+
ini=INI \
|
|
1605
|
+
ios=iOS \
|
|
1606
|
+
ip=IP \
|
|
1607
|
+
jetbrains=JetBrains \
|
|
1608
|
+
js=JS \
|
|
1609
|
+
jsdoc=JSDoc \
|
|
1610
|
+
json=JSON \
|
|
1611
|
+
jwt=JWT \
|
|
1612
|
+
k8s=K8s \
|
|
1613
|
+
kubernetes=Kubernetes \
|
|
1614
|
+
l10n=l10n \
|
|
1615
|
+
less=Less \
|
|
1616
|
+
macos=macOS \
|
|
1617
|
+
mariadb=MariaDB \
|
|
1618
|
+
markdownlint=markdownlint \
|
|
1619
|
+
md=Markdown \
|
|
1620
|
+
mdx=MDX \
|
|
1621
|
+
mfa=MFA \
|
|
1622
|
+
mongodb=MongoDB \
|
|
1623
|
+
mysql=MySQL \
|
|
1624
|
+
nestjs=NestJS \
|
|
1625
|
+
nginx=Nginx \
|
|
1626
|
+
nodejs=Node.js \
|
|
1627
|
+
npm=npm \
|
|
1628
|
+
oauth=OAuth \
|
|
1629
|
+
oidc=OIDC \
|
|
1630
|
+
opcache=OPcache \
|
|
1631
|
+
orm=ORM \
|
|
1632
|
+
os=OS \
|
|
1633
|
+
pcov=PCOV \
|
|
1634
|
+
pdo=PDO \
|
|
1635
|
+
php-cs-fixer=PHP-CS-Fixer \
|
|
1636
|
+
php=PHP \
|
|
1637
|
+
phpat=PHPat \
|
|
1638
|
+
phpdoc=PHPDoc \
|
|
1639
|
+
phpstan=PHPStan \
|
|
1640
|
+
phpstorm=PhpStorm \
|
|
1641
|
+
phpunit=PHPUnit \
|
|
1642
|
+
pnpm=pnpm \
|
|
1643
|
+
postgres=PostgreSQL \
|
|
1644
|
+
psr=PSR \
|
|
1645
|
+
qa=QA \
|
|
1646
|
+
rabbitmq=RabbitMQ \
|
|
1647
|
+
rbac=RBAC \
|
|
1648
|
+
rest=REST \
|
|
1649
|
+
roadrunner=RoadRunner \
|
|
1650
|
+
rpc=RPC \
|
|
1651
|
+
saml=SAML \
|
|
1652
|
+
sass=Sass \
|
|
1653
|
+
scss=SCSS \
|
|
1654
|
+
sdk=SDK \
|
|
1655
|
+
seo=SEO \
|
|
1656
|
+
sql=SQL \
|
|
1657
|
+
sqlite=SQLite \
|
|
1658
|
+
ssh=SSH \
|
|
1659
|
+
ssl=SSL \
|
|
1660
|
+
sso=SSO \
|
|
1661
|
+
ssr=SSR \
|
|
1662
|
+
svg=SVG \
|
|
1663
|
+
tdd=TDD \
|
|
1664
|
+
tls=TLS \
|
|
1665
|
+
toml=TOML \
|
|
1666
|
+
ts=TS \
|
|
1667
|
+
twig-cs-fixer=Twig-CS-Fixer \
|
|
1668
|
+
typedoc=TypeDoc \
|
|
1669
|
+
typescript=TypeScript \
|
|
1670
|
+
ui=UI \
|
|
1671
|
+
ulid=ULID \
|
|
1672
|
+
uri=URI \
|
|
1673
|
+
url=URL \
|
|
1674
|
+
utf8=UTF-8 \
|
|
1675
|
+
uuid=UUID \
|
|
1676
|
+
ux=UX \
|
|
1677
|
+
wasm=WASM \
|
|
1678
|
+
webpack=webpack \
|
|
1679
|
+
xhprof=XHProf \
|
|
1680
|
+
xml=XML \
|
|
1681
|
+
yaml=YAML \
|
|
1682
|
+
yarn=Yarn \
|
|
1683
|
+
yml=YAML#vv #~~ scope names title casing gets wrong, as `<scope>=<name>` pairs, `_` a space
|
|
1684
|
+
|
|
1685
|
+
_CHANGELOG_LAST_TAG = $(shell $(GIT) describe --tags --abbrev=0 2>/dev/null)
|
|
1686
|
+
_IS_CHANGELOG_ALL = $(filter all a,$(_ARG_WORDS))
|
|
1687
|
+
_IS_CHANGELOG_WRITE = $(filter write w,$(_ARG_WORDS))
|
|
1688
|
+
_IS_CHANGELOG_NOTES = $(filter notes n,$(_ARG_WORDS))
|
|
1689
|
+
_CHANGELOG_FILE = $(CHANGELOG_DIR)/$(_CHANGELOG_MAJOR).x.md
|
|
1690
|
+
_CHANGELOG_MAJOR = $(firstword $(subst ., ,$(patsubst v%,%,$(_CHANGELOG_VERSION))))
|
|
1691
|
+
_CHANGELOG_RANGE = $(if $(_IS_CHANGELOG_ALL),,$(or $(CHANGELOG_RANGE),$(if $(_CHANGELOG_LAST_TAG),$(_CHANGELOG_LAST_TAG)..HEAD)))
|
|
1692
|
+
_CHANGELOG_PARTS = $(subst .., ,$(subst ...,..,$(_CHANGELOG_RANGE)))
|
|
1693
|
+
_CHANGELOG_START = $(word 1,$(_CHANGELOG_PARTS))
|
|
1694
|
+
_CHANGELOG_END = $(word 2,$(_CHANGELOG_PARTS))
|
|
1695
|
+
_CHANGELOG_VERSION = $(shell $(GIT) describe --tags --exact-match '$(_CHANGELOG_END)' 2>/dev/null || $(PRINTF) '%s' '$(VERSION)')
|
|
1696
|
+
_CHANGELOG_COMPARE = $(if $(and $(CHANGELOG_URL),$(_CHANGELOG_START)),$(CHANGELOG_URL)/compare/$(_CHANGELOG_START)...$(_CHANGELOG_END))
|
|
1697
|
+
|
|
1698
|
+
$(call _target,changelog): #~~ prints the changelog, or writes it to `CHANGELOG_DIR` #v
|
|
1699
|
+
$(DEBUG_PREFIX)$(call _capture,$(GIT) log --no-merges --pretty=format:'%H%x1f%D%x1f%s%x1f%b%x1e' $(_CHANGELOG_RANGE)) \
|
|
1700
|
+
| _CHANGELOG_URL="$(CHANGELOG_URL)" \
|
|
1701
|
+
_CHANGELOG_VERSION="$(_CHANGELOG_VERSION)" \
|
|
1702
|
+
_CHANGELOG_COMPARE="$(_CHANGELOG_COMPARE)" \
|
|
1703
|
+
_CHANGELOG_ALL="$(_IS_CHANGELOG_ALL)" \
|
|
1704
|
+
_CHANGELOG_COMMITS="$(CHANGELOG_SKIPPED_COMMITS)" \
|
|
1705
|
+
_CHANGELOG_TYPES="$(CHANGELOG_SKIPPED_TYPES)" \
|
|
1706
|
+
_CHANGELOG_MAJOR="$(if $(_IS_CHANGELOG_WRITE),$(_CHANGELOG_MAJOR))" \
|
|
1707
|
+
_CHANGELOG_GROUPS="$(CHANGELOG_GROUPS)" \
|
|
1708
|
+
_CHANGELOG_NAMES="$(CHANGELOG_NAMES)" \
|
|
1709
|
+
$(AWK) "$$_CHANGELOG_AWK" \
|
|
1710
|
+
| { $(if $(_IS_CHANGELOG_WRITE),$(call _changelog_write),$(if $(_IS_CHANGELOG_NOTES),$(call _changelog_notes),$(call _changelog_print))); }
|
|
1711
|
+
|
|
1712
|
+
#--- common
|
|
1713
|
+
|
|
1714
|
+
_FIXERS := $(foreach FIXER_NAME,$(_FIXER_NAMES),$(call _target,$(FIXER_NAME)))
|
|
1715
|
+
_DRY_RUNS := $(foreach FIXER_NAME,$(_FIXER_NAMES),$(call _target,$(FIXER_NAME)-dry-run))
|
|
1716
|
+
_ANALYZERS := $(foreach ANALYZER_NAME,$(_ANALYZER_NAMES),$(call _target,$(ANALYZER_NAME)))
|
|
1717
|
+
_TESTS := $(foreach TEST_NAME,$(_TEST_NAMES),$(call _target,$(TEST_NAME)))
|
|
1718
|
+
_UPDATES := $(foreach TEST_NAME,$(_TEST_NAMES),$(call _target,$(TEST_NAME)-update))
|
|
1719
|
+
_COVERAGE := $(foreach COVERAGE_NAME,$(_COVERAGE_NAMES),$(call _target,$(COVERAGE_NAME)-coverage))
|
|
1720
|
+
_GROUPS := $(foreach GROUP_NAME,$(_GROUP_NAMES),$(call _target,$(GROUP_NAME)-group))
|
|
1721
|
+
_PACKS := $(foreach PACK_NAME,$(_PACK_NAMES),$(call _target,$(PACK_NAME)))
|
|
1722
|
+
|
|
1723
|
+
CHECK_TARGETS ?= #v #~~ this project's own targets, run by `check`
|
|
1724
|
+
|
|
1725
|
+
$(call _target,check): #~~ runs the fixers without writing, and every analyzer
|
|
1726
|
+
$(TRACE_PREFIX)$(if $(_DRY_RUNS)$(_ANALYZERS)$(CHECK_TARGETS),:,$(call log,No tool to run.,$(COLOR_NOTICE)))
|
|
1727
|
+
$(DEBUG_PREFIX)$(if $(_DRY_RUNS)$(_ANALYZERS)$(CHECK_TARGETS),$(MAKE) $(_VERB_FLAGS) $(_DRY_RUNS) $(_ANALYZERS) $(CHECK_TARGETS),:)
|
|
1728
|
+
|
|
1729
|
+
_CI_TARGETS := $(call _target,check) \
|
|
1730
|
+
$(call _target,test)
|
|
1731
|
+
|
|
1732
|
+
CI_TARGETS ?= $(_CI_TARGETS)#vv #~~ targets `ci` runs in order
|
|
1733
|
+
|
|
1734
|
+
$(call _target,ci): #~~ runs `CI_TARGETS` one after the other #vv
|
|
1735
|
+
$(DEBUG_PREFIX)$(foreach TARGET,$(CI_TARGETS), \
|
|
1736
|
+
$(MAKE) $(_VERB_FLAGS) $(TARGET) && \
|
|
1737
|
+
):
|
|
1738
|
+
|
|
1739
|
+
$(call _target,coverage): #~~ runs the tests and fails below the minimum coverage #vv
|
|
1740
|
+
$(TRACE_PREFIX)$(if $(_COVERAGE),:,$(call log,No test to run.,$(COLOR_NOTICE)))
|
|
1741
|
+
$(DEBUG_PREFIX)$(if $(_COVERAGE),$(MAKE) $(_VERB_FLAGS) $(_COVERAGE),:)
|
|
1742
|
+
|
|
1743
|
+
_FIX_ORDER := rector+php-cs-fixer \
|
|
1744
|
+
twig-cs-fixer \
|
|
1745
|
+
eslint+stylelint+markdownlint
|
|
1746
|
+
|
|
1747
|
+
_FIX_CHAINS := $(strip $(foreach CHAIN,$(_FIX_ORDER) $(filter-out $(subst +, ,$(_FIX_ORDER)),$(_FIXER_NAMES)), \
|
|
1748
|
+
$(if $(filter $(_FIXER_NAMES),$(subst +, ,$(CHAIN))),_$(_TARGET_PREFIX)fix-$(CHAIN)) \
|
|
1749
|
+
))
|
|
1750
|
+
|
|
1751
|
+
FIX_TARGETS ?= #v #~~ this project's own targets, run by `fix`
|
|
1752
|
+
|
|
1753
|
+
$(call _target,fix): #~~ runs every fixer this project has, writing its fixes
|
|
1754
|
+
$(TRACE_PREFIX)$(if $(_FIX_CHAINS)$(FIX_TARGETS),:,$(call log,No fixer to run.,$(COLOR_NOTICE)))
|
|
1755
|
+
$(DEBUG_PREFIX)$(if $(_FIX_CHAINS)$(FIX_TARGETS),$(MAKE) $(_VERB_FLAGS) $(_FIX_CHAINS) $(FIX_TARGETS),:)
|
|
1756
|
+
|
|
1757
|
+
_$(_TARGET_PREFIX)fix-%:
|
|
1758
|
+
$(DEBUG_PREFIX)$(foreach NAME,$(filter $(_FIXER_NAMES),$(subst +, ,$*)), \
|
|
1759
|
+
$(MAKE) $(_VERB_FLAGS) $(call _target,$(NAME)) && \
|
|
1760
|
+
):
|
|
1761
|
+
|
|
1762
|
+
GROUP_TARGETS ?= #v #~~ this project's own targets, run by `group`
|
|
1763
|
+
|
|
1764
|
+
$(call _target,group): #~~ counts every tool's findings by identifier #v
|
|
1765
|
+
$(TRACE_PREFIX)$(if $(_GROUPS)$(GROUP_TARGETS),:,$(call log,No tool to run.,$(COLOR_NOTICE)))
|
|
1766
|
+
$(DEBUG_PREFIX)$(if $(_GROUPS)$(GROUP_TARGETS),$(MAKE) $(_VERB_FLAGS) $(_GROUPS) $(GROUP_TARGETS),:)
|
|
1767
|
+
|
|
1768
|
+
$(call _target,pack): $(_PACKS) #~~ packs every stack's package into `./.local` #vvv
|
|
1769
|
+
$(TRACE_PREFIX)$(if $(_PACKS),:,$(call log,No package to pack.,$(COLOR_NOTICE)))
|
|
1770
|
+
|
|
1771
|
+
TEST_TARGETS ?= #v #~~ this project's own targets, run by `test`
|
|
1772
|
+
|
|
1773
|
+
$(call _target,test): #~~ runs the tests of every stack this project has
|
|
1774
|
+
$(TRACE_PREFIX)$(if $(_TESTS)$(TEST_TARGETS),:,$(call log,No test to run.,$(COLOR_NOTICE)))
|
|
1775
|
+
$(DEBUG_PREFIX)$(if $(_TESTS)$(TEST_TARGETS),$(MAKE) $(_VERB_FLAGS) $(_TESTS) $(TEST_TARGETS),:)
|
|
1776
|
+
|
|
1777
|
+
$(call _target,test-update): #~~ runs those tests and updates their snapshots #v
|
|
1778
|
+
$(TRACE_PREFIX)$(if $(_UPDATES),:,$(call log,No test to run.,$(COLOR_NOTICE)))
|
|
1779
|
+
$(DEBUG_PREFIX)$(if $(_UPDATES),$(MAKE) $(_VERB_FLAGS) $(_UPDATES),:)
|
|
1780
|
+
|
|
1781
|
+
FIXTURE_TARGETS ?= #v #~~ this project's own targets, run by `fixtures` to build what its tests read
|
|
1782
|
+
|
|
1783
|
+
$(call _target,fixtures): #~~ builds the fixtures this project's tests read #v
|
|
1784
|
+
$(TRACE_PREFIX)$(if $(FIXTURE_TARGETS),:,$(call log,No fixture to build.,$(COLOR_NOTICE)))
|
|
1785
|
+
$(DEBUG_PREFIX)$(if $(FIXTURE_TARGETS),$(MAKE) $(_VERB_FLAGS) --no-keep-going $(FIXTURE_TARGETS),:)
|
|
1786
|
+
|
|
1787
|
+
.DEFAULT:
|
|
1788
|
+
ifeq ($(TARGET),) # unknown
|
|
1789
|
+
$(DEBUG_PREFIX)$(if $(_ALIAS_GOAL),exec $(MAKE) $(_MAKE_FLAGS) $(_ALIAS_GOAL) \
|
|
1790
|
+
$(if $(_UNKNOWN_ARGS),-- $(_UNKNOWN_ARGS)),:)
|
|
1791
|
+
$(DEBUG_PREFIX)$(if $(_ALIAS_TARGET),$(if $(_ALIAS_GOAL),:,{ \
|
|
1792
|
+
$(call log,`%s` aliases `%s`$(_COMMA) which is not a target.,$(COLOR_ERROR),'$@' '$(_ALIAS_TARGET)'); \
|
|
1793
|
+
exit $(BRNSHKR_CONFIG_ERROR_CODE); \
|
|
1794
|
+
}),:)
|
|
1795
|
+
$(DEBUG_PREFIX)$(if $(_ALIAS_TARGET),:,$(if $(_IS_FIRST_GOAL),$(call log,Unknown command `%s`.,$(COLOR_ERROR),'$@'),:))
|
|
1796
|
+
$(DEBUG_PREFIX)$(if $(_ALIAS_TARGET),:,$(if $(_IS_FIRST_GOAL),$(if $(_IS_JUST_PRINT),:,$(call _suggest)),:))
|
|
1797
|
+
else
|
|
1798
|
+
$(DEBUG_PREFIX):
|
|
1799
|
+
endif # /unknown
|
|
1800
|
+
|
|
1801
|
+
_PREFLIGHT := _$(_TARGET_PREFIX)assert-no-collisions
|
|
1802
|
+
|
|
1803
|
+
$(filter-out $(_PREFLIGHT),$(_ALL_TARGETS)): | $(_PREFLIGHT)
|
|
1804
|
+
|
|
1805
|
+
_$(_TARGET_PREFIX)assert-no-collisions:
|
|
1806
|
+
ifneq ($(_DUPLICATE_TARGETS),) # duplicates
|
|
1807
|
+
$(TRACE_PREFIX)$(foreach NAME,$(_DUPLICATE_TARGETS),$(call log,`%s` is defined in %s.,$(COLOR_ERROR),'$(NAME)' '$(call _join_as_quoted_list,$(call _named_path,$(call _definitions_of,$(NAME))))')$(_NEWLINE))
|
|
1808
|
+
$(TRACE_PREFIX)$(call log,Rename $(if $(word 2,$(_DUPLICATE_TARGETS)),them,it)$(_COMMA) since this project defines the same name twice.,$(COLOR_NOTICE))
|
|
1809
|
+
$(TRACE_PREFIX)exit $(BRNSHKR_CONFIG_ERROR_CODE)
|
|
1810
|
+
endif # /duplicates
|
|
1811
|
+
ifneq ($(and $(strip $(TARGET_PREFIX)),$(_COLLIDING_TARGETS)),) # collisions
|
|
1812
|
+
$(TRACE_PREFIX)$(foreach NAME,$(_COLLIDING_TARGETS),$(call log,`%s` is defined in %s.,$(COLOR_ERROR),'$(_TARGET_PREFIX)$(NAME)' '$(call _join_as_quoted_list,$(call _named_path,$(call _definitions_of,$(_TARGET_PREFIX)$(NAME))))')$(_NEWLINE))
|
|
1813
|
+
$(TRACE_PREFIX)$(call log,Rename $(if $(word 2,$(_COLLIDING_TARGETS)),them,it)$(_COMMA) since `TARGET_PREFIX` is already the namespace this project chose.,$(COLOR_NOTICE))
|
|
1814
|
+
$(TRACE_PREFIX)exit $(BRNSHKR_CONFIG_ERROR_CODE)
|
|
1815
|
+
endif # /collisions
|
|
1816
|
+
|
|
1817
|
+
PHONY ?= 1#v #~~ targets marked `.PHONY`, `shared` for the shared ones only or `0` for none
|
|
1818
|
+
|
|
1819
|
+
.PHONY: $(if $(filter-out $(_FALSE),$(PHONY)),$(if $(filter shared,$(PHONY)),$(_SHARED_TARGETS),$(_ALL_TARGETS)))
|
|
1820
|
+
|
|
1821
|
+
#--- ansi
|
|
1822
|
+
|
|
1823
|
+
_ANSI_MODIFIER_NORMAL := 0
|
|
1824
|
+
_ANSI_MODIFIER_BOLD := 1
|
|
1825
|
+
_ANSI_MODIFIER_UNDERLINE := 4
|
|
1826
|
+
_ANSI_MODIFIER_REVERSE := 7
|
|
1827
|
+
|
|
1828
|
+
_ANSI_COLOR_NORMAL := 0
|
|
1829
|
+
_ANSI_COLOR_BLACK := 30
|
|
1830
|
+
_ANSI_COLOR_RED := 31
|
|
1831
|
+
_ANSI_COLOR_GREEN := 32
|
|
1832
|
+
_ANSI_COLOR_YELLOW := 33
|
|
1833
|
+
_ANSI_COLOR_BLUE := 34
|
|
1834
|
+
_ANSI_COLOR_MAGENTA := 35
|
|
1835
|
+
_ANSI_COLOR_CYAN := 36
|
|
1836
|
+
_ANSI_COLOR_WHITE := 37
|
|
1837
|
+
|
|
1838
|
+
_ANSI_COLOR_BRIGHT_NORMAL := 0
|
|
1839
|
+
_ANSI_COLOR_BRIGHT_BLACK := 90
|
|
1840
|
+
_ANSI_COLOR_BRIGHT_RED := 91
|
|
1841
|
+
_ANSI_COLOR_BRIGHT_GREEN := 92
|
|
1842
|
+
_ANSI_COLOR_BRIGHT_YELLOW := 93
|
|
1843
|
+
_ANSI_COLOR_BRIGHT_BLUE := 94
|
|
1844
|
+
_ANSI_COLOR_BRIGHT_MAGENTA := 95
|
|
1845
|
+
_ANSI_COLOR_BRIGHT_CYAN := 96
|
|
1846
|
+
_ANSI_COLOR_BRIGHT_WHITE := 97
|
|
1847
|
+
|
|
1848
|
+
_ANSI_RESET = $(if $(_IS_NO_ANSI),,\033[0m)
|
|
1849
|
+
_ANSI_CLEAR_LINE = $(if $(_IS_NO_ANSI),,\033[2K)
|
|
1850
|
+
_ANSI_HIDE_CURSOR = $(if $(_IS_NO_ANSI),,\033[?25l)
|
|
1851
|
+
_ANSI_SHOW_CURSOR = $(if $(_IS_NO_ANSI),,\033[?25h)
|
|
1852
|
+
|
|
1853
|
+
_UPPER_normal := NORMAL
|
|
1854
|
+
_UPPER_black := BLACK
|
|
1855
|
+
_UPPER_red := RED
|
|
1856
|
+
_UPPER_green := GREEN
|
|
1857
|
+
_UPPER_yellow := YELLOW
|
|
1858
|
+
_UPPER_blue := BLUE
|
|
1859
|
+
_UPPER_magenta := MAGENTA
|
|
1860
|
+
_UPPER_cyan := CYAN
|
|
1861
|
+
_UPPER_white := WHITE
|
|
1862
|
+
|
|
1863
|
+
#---v helpers
|
|
1864
|
+
|
|
1865
|
+
#**
|
|
1866
|
+
#* Wraps a string in ANSI color and modifier escapes, resetting after it.
|
|
1867
|
+
#* The color and the modifiers may be given together or apart.
|
|
1868
|
+
#*
|
|
1869
|
+
#* parameters:
|
|
1870
|
+
#* text: string
|
|
1871
|
+
#* color_and_or_modifiers?: AnyOf<normal | white | black | red | yellow | green | cyan | blue | magenta | bold | underline | reverse | bright>
|
|
1872
|
+
#* modifiers?: AnyOf<normal | bold | underline | reverse | bright>
|
|
1873
|
+
#*
|
|
1874
|
+
#* returns: string
|
|
1875
|
+
#*
|
|
1876
|
+
define text
|
|
1877
|
+
$(call _ansi,$2 $3)$1$(_ANSI_RESET)
|
|
1878
|
+
endef
|
|
1879
|
+
|
|
1880
|
+
#**
|
|
1881
|
+
#* The path on this machine of a file the tools see under `WORKDIR`.
|
|
1882
|
+
#*
|
|
1883
|
+
#* parameters:
|
|
1884
|
+
#* path: string
|
|
1885
|
+
#*
|
|
1886
|
+
#* returns: string
|
|
1887
|
+
#*
|
|
1888
|
+
define _host_path
|
|
1889
|
+
$(patsubst $(WORKDIR)/%,$(CURDIR)/%,$1)
|
|
1890
|
+
endef
|
|
1891
|
+
|
|
1892
|
+
#**
|
|
1893
|
+
#* Turns a list of extensions into the brace glob a tool is handed.
|
|
1894
|
+
#*
|
|
1895
|
+
#* parameters:
|
|
1896
|
+
#* extensions: list<string>
|
|
1897
|
+
#*
|
|
1898
|
+
#* returns: string
|
|
1899
|
+
#*
|
|
1900
|
+
define _brace_glob
|
|
1901
|
+
**/*.{$(subst $(_SPACE),$(_COMMA),$(strip $1))}
|
|
1902
|
+
endef
|
|
1903
|
+
|
|
1904
|
+
#**
|
|
1905
|
+
#* Whether `--dry-run` is among the given words.
|
|
1906
|
+
#* The Makefile's own pseudo-flag, since none of these fixers understand it natively; its presence
|
|
1907
|
+
#* tells the plain target to skip `--fix`.
|
|
1908
|
+
#*
|
|
1909
|
+
#* parameters:
|
|
1910
|
+
#* words: list<string>
|
|
1911
|
+
#*
|
|
1912
|
+
#* returns: string
|
|
1913
|
+
#*
|
|
1914
|
+
define _is_dry_run
|
|
1915
|
+
$(filter --dry-run,$1)
|
|
1916
|
+
endef
|
|
1917
|
+
|
|
1918
|
+
#**
|
|
1919
|
+
#* The given words with the Makefile's own `--dry-run` pseudo-flag removed, so it never reaches the tool.
|
|
1920
|
+
#*
|
|
1921
|
+
#* parameters:
|
|
1922
|
+
#* words: list<string>
|
|
1923
|
+
#*
|
|
1924
|
+
#* returns: list<string>
|
|
1925
|
+
#*
|
|
1926
|
+
define _without_dry_run
|
|
1927
|
+
$(filter-out --dry-run,$1)
|
|
1928
|
+
endef
|
|
1929
|
+
|
|
1930
|
+
#**
|
|
1931
|
+
#* Whether the given words contain a real positional argument.
|
|
1932
|
+
#* Not a flag, and not the value a preceding flag from the second list consumes.
|
|
1933
|
+
#*
|
|
1934
|
+
#* parameters:
|
|
1935
|
+
#* words: list<string>
|
|
1936
|
+
#* value_flags: list<string>
|
|
1937
|
+
#*
|
|
1938
|
+
#* returns: string
|
|
1939
|
+
#*
|
|
1940
|
+
define _has_positional_arg
|
|
1941
|
+
$(strip $(shell $(PRINTF) '%s\n' $1 | $(AWK) -v _VALUE_FLAGS=' $(strip $2) ' '\
|
|
1942
|
+
{ \
|
|
1943
|
+
if (skip) { skip = 0; next } \
|
|
1944
|
+
if (index(_VALUE_FLAGS, " " $$0 " ") > 0) { skip = 1; next } \
|
|
1945
|
+
if ($$0 != "" && substr($$0, 1, 1) != "-") { print 1; exit } \
|
|
1946
|
+
}'))
|
|
1947
|
+
endef
|
|
1948
|
+
|
|
1949
|
+
#**
|
|
1950
|
+
#* The name a config answers to, which is its file name without a leading dot, a `.dist` or a suffix.
|
|
1951
|
+
#*
|
|
1952
|
+
#* parameters:
|
|
1953
|
+
#* path: string
|
|
1954
|
+
#*
|
|
1955
|
+
#* returns: string
|
|
1956
|
+
#*
|
|
1957
|
+
define _config_name
|
|
1958
|
+
$(firstword $(subst ., ,$(patsubst .%,%,$(notdir $(lastword $(subst |, ,$1))))))
|
|
1959
|
+
endef
|
|
1960
|
+
|
|
1961
|
+
#**
|
|
1962
|
+
#* The configs a run writes, which is every one of them unless the run named some.
|
|
1963
|
+
#*
|
|
1964
|
+
#* parameters:
|
|
1965
|
+
#* configs: list<string>
|
|
1966
|
+
#*
|
|
1967
|
+
#* returns: list<string>
|
|
1968
|
+
#*
|
|
1969
|
+
define _selected_configs
|
|
1970
|
+
$(if $(_NAMED_CONFIGS),$(foreach CONFIG,$1,$(if $(filter $(call _config_name,$(CONFIG)),$(_NAMED_CONFIGS)),$(CONFIG))),$1)
|
|
1971
|
+
endef
|
|
1972
|
+
|
|
1973
|
+
#**
|
|
1974
|
+
#* The tracked half of a config path, which is the path itself once it names the `.dist` file.
|
|
1975
|
+
#*
|
|
1976
|
+
#* parameters:
|
|
1977
|
+
#* path: string
|
|
1978
|
+
#*
|
|
1979
|
+
#* returns: string
|
|
1980
|
+
#*
|
|
1981
|
+
define _tracked
|
|
1982
|
+
$(if $(findstring .dist.,$1),$1,$(basename $1).dist$(suffix $1))
|
|
1983
|
+
endef
|
|
1984
|
+
|
|
1985
|
+
#**
|
|
1986
|
+
#* The private half beside wherever the tool resolved its config.
|
|
1987
|
+
#* Empty where it resolved something outside that pair — an `eslint.ts` rather than either half of
|
|
1988
|
+
#* the `.mjs` one.
|
|
1989
|
+
#*
|
|
1990
|
+
#* parameters:
|
|
1991
|
+
#* resolved: string
|
|
1992
|
+
#* extension: string
|
|
1993
|
+
#*
|
|
1994
|
+
#* returns: string
|
|
1995
|
+
#*
|
|
1996
|
+
define _layered
|
|
1997
|
+
$(if $(filter %.$2 %.dist.$2,$1),$(subst .dist.,.,$1))
|
|
1998
|
+
endef
|
|
1999
|
+
|
|
2000
|
+
#**
|
|
2001
|
+
#* The same paths, rooted in the project's own `conf/`.
|
|
2002
|
+
#* Anything the config search resolved inside an installation of this package moves back, so a write
|
|
2003
|
+
#* never lands in `vendor/` or `node_modules/`.
|
|
2004
|
+
#*
|
|
2005
|
+
#* parameters:
|
|
2006
|
+
#* paths: list<string>
|
|
2007
|
+
#*
|
|
2008
|
+
#* returns: list<string>
|
|
2009
|
+
#*
|
|
2010
|
+
define _in_project
|
|
2011
|
+
$(foreach CONFIG,$1,$(if $(filter $(addsuffix /%,$(_PACKAGE_CONF_DIRS)),$(CONFIG)),$(WORKDIR)/conf/$(notdir $(CONFIG)),$(CONFIG)))
|
|
2012
|
+
endef
|
|
2013
|
+
|
|
2014
|
+
#**
|
|
2015
|
+
#* Names the project's root namespace in a copied PHP config's `@internal` tag.
|
|
2016
|
+
#* A file with no namespace still says where it belongs. Falls back to `App`, and does nothing
|
|
2017
|
+
#* without an autoloader.
|
|
2018
|
+
#*
|
|
2019
|
+
#* parameters:
|
|
2020
|
+
#* path: string
|
|
2021
|
+
#*
|
|
2022
|
+
#* returns: void
|
|
2023
|
+
#*
|
|
2024
|
+
define _name_internal_tag
|
|
2025
|
+
if [ -f "$(call _host_path,$(_AUTOLOADER))" ]; then \
|
|
2026
|
+
$(PHP) -r "$$_NAME_INTERNAL_TAG_PROGRAM" "$(_AUTOLOADER)" "$(WORKDIR)/$1" \
|
|
2027
|
+
|| exit $(BRNSHKR_CONFIG_ERROR_CODE); \
|
|
2028
|
+
fi
|
|
2029
|
+
endef
|
|
2030
|
+
|
|
2031
|
+
#**
|
|
2032
|
+
#* A list in reverse order.
|
|
2033
|
+
#*
|
|
2034
|
+
#* parameters:
|
|
2035
|
+
#* items: list<string>
|
|
2036
|
+
#*
|
|
2037
|
+
#* returns: list<string>
|
|
2038
|
+
#*
|
|
2039
|
+
define _reverse
|
|
2040
|
+
$(if $1,$(call _reverse,$(wordlist 2,$(words $1),$1)) $(firstword $1))
|
|
2041
|
+
endef
|
|
2042
|
+
|
|
2043
|
+
#**
|
|
2044
|
+
#* The directories a config is looked for in, most specific first.
|
|
2045
|
+
#* `$(CURDIR)` is deliberately absent: a bare config at the repository root would re-enter PHPStan's
|
|
2046
|
+
#* dynamically evaluated paths.
|
|
2047
|
+
#*
|
|
2048
|
+
#* parameters:
|
|
2049
|
+
#* stack: string
|
|
2050
|
+
#*
|
|
2051
|
+
#* returns: list<string>
|
|
2052
|
+
#*
|
|
2053
|
+
define _config_dirs
|
|
2054
|
+
$(foreach BASE,$(call _reverse,$(_SEARCH_BASES)),$(if $1,$(WORKDIR)/$(BASE)/$1) $(WORKDIR)/$(BASE))
|
|
2055
|
+
endef
|
|
2056
|
+
|
|
2057
|
+
#**
|
|
2058
|
+
#* The candidate names this package ships, which is the tracked half alone.
|
|
2059
|
+
#*
|
|
2060
|
+
#* parameters:
|
|
2061
|
+
#* candidates: list<string>
|
|
2062
|
+
#*
|
|
2063
|
+
#* returns: list<string>
|
|
2064
|
+
#*
|
|
2065
|
+
define _shipped_names
|
|
2066
|
+
$(foreach NAME,$(notdir $1),$(if $(findstring .dist.,$(NAME)),$(NAME)))
|
|
2067
|
+
endef
|
|
2068
|
+
|
|
2069
|
+
#**
|
|
2070
|
+
#* The configs a directory has, in the order the names were given.
|
|
2071
|
+
#*
|
|
2072
|
+
#* parameters:
|
|
2073
|
+
#* directory: string
|
|
2074
|
+
#* names: list<string>
|
|
2075
|
+
#*
|
|
2076
|
+
#* returns: list<string>
|
|
2077
|
+
#*
|
|
2078
|
+
define _configs_in
|
|
2079
|
+
$(foreach NAME,$2,$(if $(wildcard $(call _host_path,$1/$(NAME))),$1/$(NAME)))
|
|
2080
|
+
endef
|
|
2081
|
+
|
|
2082
|
+
#**
|
|
2083
|
+
#* The first config in the list that is there.
|
|
2084
|
+
#* Then the tracked name under either installation of this package, so a project reads its own file,
|
|
2085
|
+
#* falls back to the tracked one, and may keep neither. The last candidate is what a project is told
|
|
2086
|
+
#* to create when it has none, and `CONFIG` pins the choice.
|
|
2087
|
+
#*
|
|
2088
|
+
#* parameters:
|
|
2089
|
+
#* candidates: list<string>
|
|
2090
|
+
#* stack: string
|
|
2091
|
+
#*
|
|
2092
|
+
#* returns: string
|
|
2093
|
+
#*
|
|
2094
|
+
define _config
|
|
2095
|
+
$(if $(_PINNED_CONFIG),$(if $(filter local,$(_PINNED_CONFIG)),$(firstword $1),$(lastword $1)),$(or \
|
|
2096
|
+
$(firstword $(foreach DIRECTORY,$(call _config_dirs,$2),$(call _configs_in,$(DIRECTORY),$(notdir $1))) \
|
|
2097
|
+
$(foreach DIRECTORY,$(_PACKAGE_CONF_DIRS),$(call _configs_in,$(DIRECTORY),$(call _shipped_names,$1)))),$(lastword $1)))
|
|
2098
|
+
endef
|
|
2099
|
+
|
|
2100
|
+
#**
|
|
2101
|
+
#* The path a message names it by: relative to the checkout, the way the reader would type it.
|
|
2102
|
+
#*
|
|
2103
|
+
#* parameters:
|
|
2104
|
+
#* path: string
|
|
2105
|
+
#*
|
|
2106
|
+
#* returns: string
|
|
2107
|
+
#*
|
|
2108
|
+
define _named_path
|
|
2109
|
+
$(patsubst $(CURDIR)/%,./%,$(call _host_path,$1))
|
|
2110
|
+
endef
|
|
2111
|
+
|
|
2112
|
+
#**
|
|
2113
|
+
#* Offers the closest targets to what was typed, asking before running the only one where there is one.
|
|
2114
|
+
#*
|
|
2115
|
+
#* parameters:
|
|
2116
|
+
#* prefix?: string
|
|
2117
|
+
#*
|
|
2118
|
+
#* returns: void
|
|
2119
|
+
#*
|
|
2120
|
+
define _suggest
|
|
2121
|
+
$(if $(_SUGGESTED_TARGETS),$(if $(word 2,$(_SUGGESTED_TARGETS)), \
|
|
2122
|
+
$(call log,Did you mean %s?,$(COLOR_NOTICE),'$(call _join_as_quoted_list,$(addprefix $1,$(_SUGGESTED_TARGETS)),disjunction)'); \
|
|
2123
|
+
exit $(BRNSHKR_CONFIG_ERROR_CODE),if [ -t 0 ] && [ -z "$(_IS_CI)" ]; then \
|
|
2124
|
+
$(call confirm,Did you mean `$1$(_SUGGESTED_TARGETS)`?) || exit $(BRNSHKR_CONFIG_ERROR_CODE); \
|
|
2125
|
+
exec $(_DOTENV_RESET)$(MAKE) $(_MAKE_FLAGS) $(_INHERITED_FLAGS) $1$(_SUGGESTED_TARGETS) \
|
|
2126
|
+
$(if $(_UNKNOWN_ARGS),-- $(_UNKNOWN_ARGS)); \
|
|
2127
|
+
fi; \
|
|
2128
|
+
$(call log,Did you mean `%s`?,$(COLOR_NOTICE),'$1$(_SUGGESTED_TARGETS)'); \
|
|
2129
|
+
exit $(BRNSHKR_CONFIG_ERROR_CODE)),$(call log,Run `make $(call _target,help)` or just `make` to see available commands.,$(COLOR_NOTICE)); \
|
|
2130
|
+
exit $(BRNSHKR_CONFIG_ERROR_CODE))
|
|
2131
|
+
endef
|
|
2132
|
+
|
|
2133
|
+
#**
|
|
2134
|
+
#* Fails the recipe when an argument is not one of the values the target accepts.
|
|
2135
|
+
#*
|
|
2136
|
+
#* parameters:
|
|
2137
|
+
#* noun: string
|
|
2138
|
+
#*
|
|
2139
|
+
#* returns: void
|
|
2140
|
+
#*
|
|
2141
|
+
define _require_known_value
|
|
2142
|
+
$(if $(_UNKNOWN_VALUES),{ \
|
|
2143
|
+
$(call log,No $1 named `%s`.,$(COLOR_ERROR),'$(_MISTYPED_VALUE)'); \
|
|
2144
|
+
$(if $(_SUGGESTED_VALUES),$(if $(word 2,$(_SUGGESTED_VALUES)), \
|
|
2145
|
+
$(call log,Did you mean %s?,$(COLOR_NOTICE),'$(call _join_as_quoted_list,$(_SUGGESTED_VALUES),disjunction)'), \
|
|
2146
|
+
$(call log,Did you mean `%s`?,$(COLOR_NOTICE),'$(_SUGGESTED_VALUES)')), \
|
|
2147
|
+
$(call log,Try %s.,$(COLOR_NOTICE),'$(call _join_as_quoted_list,$(_ARG_VALUES),disjunction)',no-autolink)); \
|
|
2148
|
+
exit $(BRNSHKR_CONFIG_ERROR_CODE); \
|
|
2149
|
+
},:)
|
|
2150
|
+
endef
|
|
2151
|
+
|
|
2152
|
+
#**
|
|
2153
|
+
#* Writes each `GIT_HOOKS` pair into this checkout's git hooks, keeping what is there.
|
|
2154
|
+
#*
|
|
2155
|
+
#* parameters:
|
|
2156
|
+
#* replace?: OneOf<yes | no> = no
|
|
2157
|
+
#*
|
|
2158
|
+
#* returns: void
|
|
2159
|
+
#*
|
|
2160
|
+
define _install_git_hooks
|
|
2161
|
+
$(GIT) rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0; \
|
|
2162
|
+
directory="$$($(GIT) rev-parse --git-path hooks)"; \
|
|
2163
|
+
$(MKDIR) -p "$$directory"; \
|
|
2164
|
+
for pair in $(GIT_HOOKS); do \
|
|
2165
|
+
hook="$${pair%%=*}"; \
|
|
2166
|
+
$(if $(filter yes,$1),,[ ! -f "$$directory/$$hook" ] || continue;) \
|
|
2167
|
+
$(PRINTF) '#!/bin/sh\nexec %s %s\n' '$(MAKE)' "$${pair#*=}" > "$$directory/$$hook"; \
|
|
2168
|
+
$(CHMOD) +x "$$directory/$$hook"; \
|
|
2169
|
+
$(call log,Installed `%s`.,$(COLOR_SUCCESS),"$$hook"); \
|
|
2170
|
+
done
|
|
2171
|
+
endef
|
|
2172
|
+
|
|
2173
|
+
#**
|
|
2174
|
+
#* Fails the recipe when a configured file is not there, naming the variable it came from.
|
|
2175
|
+
#*
|
|
2176
|
+
#* parameters:
|
|
2177
|
+
#* path: string
|
|
2178
|
+
#* variable: string
|
|
2179
|
+
#*
|
|
2180
|
+
#* returns: void
|
|
2181
|
+
#*
|
|
2182
|
+
define _require_file
|
|
2183
|
+
test -f '$(call _host_path,$1)' || { \
|
|
2184
|
+
$(call log,`%s` is missing$(_COMMA) set `%s` or create it.,$(COLOR_ERROR),'$(call _named_path,$1)' '$2') >&2; \
|
|
2185
|
+
exit $(BRNSHKR_CONFIG_ERROR_CODE); \
|
|
2186
|
+
}
|
|
2187
|
+
endef
|
|
2188
|
+
|
|
2189
|
+
_IS_GROUP_PARALLEL = $(if $(filter -O%,$(MAKEFLAGS)),,$(_IS_PARALLEL))
|
|
2190
|
+
_HAS_GROUP_LABEL = $(and $(_IS_ANNOUNCING),$(ANNOUNCEMENT))
|
|
2191
|
+
_CLEAR_SPINNER = $(PRINTF) "\r$(_ANSI_CLEAR_LINE)$(_ANSI_SHOW_CURSOR)"
|
|
2192
|
+
_SPINNER_SECONDS := 0.15
|
|
2193
|
+
_SIGNAL_INTERRUPT := INT
|
|
2194
|
+
_SIGNAL_TERMINATE := TERM
|
|
2195
|
+
_SIGNAL_HANGUP := HUP
|
|
2196
|
+
_INTERRUPT_EXIT_CODE := 130
|
|
2197
|
+
_SPINNER_SIGNALS := $(_SIGNAL_INTERRUPT) $(_SIGNAL_TERMINATE) $(_SIGNAL_HANGUP)
|
|
2198
|
+
|
|
2199
|
+
#**
|
|
2200
|
+
#* Starts a spinner in the background, which runs until the shell that started it is gone.
|
|
2201
|
+
#*
|
|
2202
|
+
#* parameters:
|
|
2203
|
+
#* indent?: string
|
|
2204
|
+
#*
|
|
2205
|
+
#* returns: void
|
|
2206
|
+
#*
|
|
2207
|
+
define __spinner
|
|
2208
|
+
while kill -0 $$$$ 2>/dev/null; do for frame in '|' '/' '-' '\'; do \
|
|
2209
|
+
$(PRINTF) '\r$1$(call text,%s Running…,$(COLOR_DESCRIPTION))' "$$frame"; \
|
|
2210
|
+
sleep $(_SPINNER_SECONDS) 2>/dev/null || sleep 1; \
|
|
2211
|
+
done; done & spinner=$$!
|
|
2212
|
+
endef
|
|
2213
|
+
|
|
2214
|
+
#**
|
|
2215
|
+
#* Runs a tool and prints its findings grouped by identifier, failing only when it reported nothing.
|
|
2216
|
+
#*
|
|
2217
|
+
#* parameters:
|
|
2218
|
+
#* command: string
|
|
2219
|
+
#* identifier: string
|
|
2220
|
+
#* message?: string
|
|
2221
|
+
#*
|
|
2222
|
+
#* returns: void
|
|
2223
|
+
#*
|
|
2224
|
+
define _group
|
|
2225
|
+
label=$(if $(_HAS_GROUP_LABEL),$$($(call log,$(ANNOUNCEMENT),$(COLOR_NOTICE),'$@'))); \
|
|
2226
|
+
has_spinner=; $(if $(or $(_IS_GROUP_PARALLEL),$(_IS_NO_ANSI)),,[ -t 1 ] && has_spinner=1;) \
|
|
2227
|
+
$(if $(_IS_GROUP_PARALLEL),,[ -z "$$label" ] || $(PRINTF) '%s\n' "$$label"; \
|
|
2228
|
+
[ -z "$$has_spinner" ] || { \
|
|
2229
|
+
$(PRINTF) '$(_ANSI_HIDE_CURSOR)'; \
|
|
2230
|
+
trap 'kill $$spinner 2>/dev/null; $(_CLEAR_SPINNER); exit $(_INTERRUPT_EXIT_CODE)' $(_SPINNER_SIGNALS); \
|
|
2231
|
+
$(call __spinner,$(if $(_HAS_GROUP_LABEL), )); \
|
|
2232
|
+
};) \
|
|
2233
|
+
output=$$($1 2>&1); status=$$?; \
|
|
2234
|
+
report=$$($(PRINTF) '%s\n' "$$output" \
|
|
2235
|
+
| $(AWK) -v _INDENT='$(if $(_HAS_GROUP_LABEL),$(INDENT),0)' -v _IDENTIFIER='$2' -v _MESSAGE='$3' -v _STATUS="$$status" "$$_GROUP_AWK"); code=$$?; \
|
|
2236
|
+
$(if $(_IS_GROUP_PARALLEL),[ -z "$$report" ] || $(PRINTF) '%s\n' $${label:+"$$label"} "$$report";, \
|
|
2237
|
+
[ -z "$$has_spinner" ] || { \
|
|
2238
|
+
kill $$spinner 2>/dev/null; \
|
|
2239
|
+
wait $$spinner 2>/dev/null; \
|
|
2240
|
+
$(_CLEAR_SPINNER); \
|
|
2241
|
+
}; \
|
|
2242
|
+
[ -z "$$report" ] || $(PRINTF) '%s\n' "$$report";) \
|
|
2243
|
+
exit $$code
|
|
2244
|
+
endef
|
|
2245
|
+
|
|
2246
|
+
#**
|
|
2247
|
+
#* Runs a tool behind a spinner, which clears as soon as the tool prints anything.
|
|
2248
|
+
#*
|
|
2249
|
+
#* parameters:
|
|
2250
|
+
#* command: string
|
|
2251
|
+
#*
|
|
2252
|
+
#* returns: void
|
|
2253
|
+
#*
|
|
2254
|
+
define _spinner
|
|
2255
|
+
spin_command() { $1; }; \
|
|
2256
|
+
has_spinner=; $(if $(or $(_IS_PARALLEL),$(_IS_NO_ANSI)),,[ -t 1 ] \
|
|
2257
|
+
&& [ -n '$(SCRIPT)' ] \
|
|
2258
|
+
&& command -v $(SCRIPT) >/dev/null 2>&1 \
|
|
2259
|
+
&& has_spinner=1;) \
|
|
2260
|
+
if [ -z "$$has_spinner" ]; then \
|
|
2261
|
+
spin_command; \
|
|
2262
|
+
else \
|
|
2263
|
+
export _SPIN_STATUS=$$($(MKTEMP)); \
|
|
2264
|
+
script_command='($(subst $(_QUOTE),$(_QUOTE)\$(_QUOTE)$(_QUOTE),$1)); $(PRINTF) "%s" $$? > "$$_SPIN_STATUS"'; \
|
|
2265
|
+
if $(SCRIPT) --version >/dev/null 2>&1; then \
|
|
2266
|
+
set -- -qfc "$$script_command" /dev/null; \
|
|
2267
|
+
else \
|
|
2268
|
+
set -- -q /dev/null $(SHELL) -c "$$script_command"; \
|
|
2269
|
+
fi; \
|
|
2270
|
+
$(PRINTF) '$(_ANSI_HIDE_CURSOR)'; \
|
|
2271
|
+
trap '$(_CLEAR_SPINNER); exit $(_INTERRUPT_EXIT_CODE)' $(_SPINNER_SIGNALS); \
|
|
2272
|
+
$(call __spinner); \
|
|
2273
|
+
SHELL='$(SHELL)' $(SCRIPT) "$$@" | { \
|
|
2274
|
+
IFS= read -r line; has_line=$$?; \
|
|
2275
|
+
kill $$spinner 2>/dev/null; \
|
|
2276
|
+
wait $$spinner 2>/dev/null; \
|
|
2277
|
+
$(_CLEAR_SPINNER); \
|
|
2278
|
+
[ $$has_line -ne 0 ] || $(PRINTF) '%s\n' "$$line"; \
|
|
2279
|
+
$(CAT); \
|
|
2280
|
+
}; \
|
|
2281
|
+
code=$$($(CAT) "$$_SPIN_STATUS"); \
|
|
2282
|
+
$(RM) -f "$$_SPIN_STATUS"; \
|
|
2283
|
+
[ -n "$$code" ] || { spin_command; exit $$?; }; \
|
|
2284
|
+
exit $$code; \
|
|
2285
|
+
fi
|
|
2286
|
+
endef
|
|
2287
|
+
|
|
2288
|
+
#**
|
|
2289
|
+
#* Prints a command's output for a pipe to read, failing the recipe when the command itself failed.
|
|
2290
|
+
#*
|
|
2291
|
+
#* parameters:
|
|
2292
|
+
#* command: string
|
|
2293
|
+
#*
|
|
2294
|
+
#* returns: void
|
|
2295
|
+
#*
|
|
2296
|
+
define _capture
|
|
2297
|
+
output=$$($1) || { $(PRINTF) '%s\n' "$$output" >&2; exit $(BRNSHKR_CONFIG_ERROR_CODE); }; $(PRINTF) '%s\n' "$$output"
|
|
2298
|
+
endef
|
|
2299
|
+
|
|
2300
|
+
#**
|
|
2301
|
+
#* Fails the recipe when one metric of a text coverage report falls short, as every runner but Pest needs.
|
|
2302
|
+
#*
|
|
2303
|
+
#* parameters:
|
|
2304
|
+
#* path: string
|
|
2305
|
+
#* metric: string
|
|
2306
|
+
#* minimum: number
|
|
2307
|
+
#* variable: string
|
|
2308
|
+
#*
|
|
2309
|
+
#* returns: void
|
|
2310
|
+
#*
|
|
2311
|
+
define _require_coverage
|
|
2312
|
+
$(TRACE_PREFIX)$(call _require_file,$1,$4)
|
|
2313
|
+
$(TRACE_PREFIX)$(AWK) -v _METRIC='$2' -v _MINIMUM='$3' '$$0 ~ _METRIC && !found { if (match($$0, /[0-9]+(\.[0-9]+)?%/)) { reached = substr($$0, RSTART, RLENGTH - 1) + 0; found = 1 } } END { exit reached < _MINIMUM + 0 }' '$1' || { \
|
|
2314
|
+
$(call log,`%s` coverage is below `%s%%`.,$(COLOR_ERROR),'$2' '$3') >&2; \
|
|
2315
|
+
exit $(BRNSHKR_CONFIG_ERROR_CODE); \
|
|
2316
|
+
}
|
|
2317
|
+
endef
|
|
2318
|
+
|
|
2319
|
+
#**
|
|
2320
|
+
#* A list of words, each quoted so the shell reads it as one argument.
|
|
2321
|
+
#*
|
|
2322
|
+
#* parameters:
|
|
2323
|
+
#* words: list<string>
|
|
2324
|
+
#*
|
|
2325
|
+
#* returns: list<string>
|
|
2326
|
+
#*
|
|
2327
|
+
define _shell_quoted
|
|
2328
|
+
$(foreach WORD,$1,'$(subst $(_QUOTE),$(_QUOTE)\$(_QUOTE)$(_QUOTE),$(WORD))')
|
|
2329
|
+
endef
|
|
2330
|
+
|
|
2331
|
+
#**
|
|
2332
|
+
#* Joins a list the way `Str::joinAsQuotedList()` and its JavaScript twin do, so the three read alike.
|
|
2333
|
+
#*
|
|
2334
|
+
#* parameters:
|
|
2335
|
+
#* items: list<string>
|
|
2336
|
+
#* type?: OneOf<conjunction | disjunction> = conjunction
|
|
2337
|
+
#*
|
|
2338
|
+
#* returns: string
|
|
2339
|
+
#*
|
|
2340
|
+
define _join_as_quoted_list
|
|
2341
|
+
$(if $(word 2,$1),$(subst $(_SPACE),$(_COMMA)$(_SPACE),$(foreach \
|
|
2342
|
+
ITEM,$(filter-out $(lastword $1),$1),`$(ITEM)`)) $(if \
|
|
2343
|
+
$(filter disjunction,$2),or,and) `$(lastword $1)`,$(if \
|
|
2344
|
+
$1,`$1`))
|
|
2345
|
+
endef
|
|
2346
|
+
|
|
2347
|
+
#**
|
|
2348
|
+
#* Asks a yes/no question before something destructive happens.
|
|
2349
|
+
#* It answers itself where nobody can: under `CI`, or without a terminal to ask at, it answers the default.
|
|
2350
|
+
#* It succeeds on yes, so the caller reads it as a condition and says what it skipped.
|
|
2351
|
+
#*
|
|
2352
|
+
#* parameters:
|
|
2353
|
+
#* question: string
|
|
2354
|
+
#* default?: OneOf<yes | no> = no
|
|
2355
|
+
#*
|
|
2356
|
+
#* returns: void
|
|
2357
|
+
#*
|
|
2358
|
+
define confirm
|
|
2359
|
+
{ \
|
|
2360
|
+
if [ -n "$(_IS_CI)" ] || ! ( : < /dev/tty ) 2>/dev/null; then \
|
|
2361
|
+
$(if $(filter yes,$2),true,false); \
|
|
2362
|
+
else \
|
|
2363
|
+
$(PRINTF) '%s $(call text,[$(if $(filter yes,$2),Y/n,y/N)],$(COLOR_NOTICE)) ' '$1' \
|
|
2364
|
+
| $(call _render_inline_code,printf "%s"$(_COMMA) $$0,yes) >&2 \
|
|
2365
|
+
&& trap '$(PRINTF) "\n" >&2; trap - $(_SIGNAL_INTERRUPT); kill -$(_SIGNAL_INTERRUPT) $$$$' $(_SIGNAL_INTERRUPT) \
|
|
2366
|
+
&& read -r answer < /dev/tty \
|
|
2367
|
+
&& trap - $(_SIGNAL_INTERRUPT) \
|
|
2368
|
+
&& case "$$answer" in \
|
|
2369
|
+
[yY]|[yY][eE][sS]) true;; \
|
|
2370
|
+
"") $(if $(filter yes,$2),true,false);; \
|
|
2371
|
+
*) false;; \
|
|
2372
|
+
esac; \
|
|
2373
|
+
fi; \
|
|
2374
|
+
}
|
|
2375
|
+
endef
|
|
2376
|
+
|
|
2377
|
+
#**
|
|
2378
|
+
#* Prints a message behind the `<VENDOR>/<PACKAGE>` label.
|
|
2379
|
+
#* Anything in backticks renders as inline code. The message is a `printf` format, so a value goes
|
|
2380
|
+
#* in as an argument rather than being pasted in. A literal comma has to be written as `$(_COMMA)`.
|
|
2381
|
+
#* A backticked span that names a target or an existing path links to it, which `no-autolink` turns
|
|
2382
|
+
#* off where the sentence is about neither.
|
|
2383
|
+
#*
|
|
2384
|
+
#* parameters:
|
|
2385
|
+
#* message: string
|
|
2386
|
+
#* color_and_or_modifiers?: AnyOf<normal | white | black | red | yellow | green | cyan | blue | magenta | bold | underline | reverse | bright>
|
|
2387
|
+
#* arguments?: list<string>
|
|
2388
|
+
#* autolink?: OneOf<autolink | no-autolink> = autolink
|
|
2389
|
+
#*
|
|
2390
|
+
#* returns: void
|
|
2391
|
+
#*
|
|
2392
|
+
define log
|
|
2393
|
+
$(PRINTF) '$(call text,[$(_LABEL)],$2) $1\n' $3 \
|
|
2394
|
+
| $(call _render_inline_code,print,$(if $(filter no-autolink,$4),,yes))
|
|
2395
|
+
endef
|
|
2396
|
+
|
|
2397
|
+
#**
|
|
2398
|
+
#* Renders every backticked span of a line as inline code, ending with the given awk action.
|
|
2399
|
+
#* A span links to the target or the path it names only where the caller asks for it.
|
|
2400
|
+
#*
|
|
2401
|
+
#* parameters:
|
|
2402
|
+
#* action: string
|
|
2403
|
+
#* autolink?: OneOf<yes | no> = no
|
|
2404
|
+
#*
|
|
2405
|
+
#* returns: void
|
|
2406
|
+
#*
|
|
2407
|
+
define _render_inline_code
|
|
2408
|
+
$(AWK) -v _OPEN='$(call _ansi,$(COLOR_HIGHLIGHT))' \
|
|
2409
|
+
-v _CLOSE='$(_ANSI_RESET)' \
|
|
2410
|
+
-v _URL='$(if $(filter yes,$2),$(_INLINE_CODE_URL))' \
|
|
2411
|
+
-v _CWD='$(CURDIR)' \
|
|
2412
|
+
-v _PREFIX='$(_TARGET_PREFIX)' \
|
|
2413
|
+
-v _STAGES=' $(_DOTENV_STAGES) ' \
|
|
2414
|
+
-v _TARGETS=' $(if $(filter yes,$2),$(_TARGET_LINKS)) ' ' \
|
|
2415
|
+
function render_url(url, path, line, placeholder_position) { \
|
|
2416
|
+
placeholder_position = index(url, "{file}"); \
|
|
2417
|
+
url = substr(url, 1, placeholder_position - 1) path substr(url, placeholder_position + 6); \
|
|
2418
|
+
placeholder_position = index(url, "{line}"); \
|
|
2419
|
+
if (placeholder_position) url = substr(url, 1, placeholder_position - 1) line substr(url, placeholder_position + 6); \
|
|
2420
|
+
return url \
|
|
2421
|
+
} \
|
|
2422
|
+
function target_definition(name, key, position, definition, dash) { \
|
|
2423
|
+
key = name; \
|
|
2424
|
+
position = index(_TARGETS, " " key "="); \
|
|
2425
|
+
if (!position && _PREFIX != "" && index(key, _PREFIX) == 1) { \
|
|
2426
|
+
key = substr(key, length(_PREFIX) + 1); \
|
|
2427
|
+
position = index(_TARGETS, " " key "=") \
|
|
2428
|
+
} \
|
|
2429
|
+
dash = index(key, "-"); \
|
|
2430
|
+
if (!position && dash > 1 && index(_STAGES, " " substr(key, 1, dash - 1) " ")) { \
|
|
2431
|
+
key = substr(key, dash + 1); \
|
|
2432
|
+
position = index(_TARGETS, " " key "=") \
|
|
2433
|
+
} \
|
|
2434
|
+
if (!position) return ""; \
|
|
2435
|
+
definition = substr(_TARGETS, position + length(key) + 2); \
|
|
2436
|
+
return substr(definition, 1, index(definition, " ") - 1) \
|
|
2437
|
+
} \
|
|
2438
|
+
function render_hyperlink(path, line, absolute_path, definition) { \
|
|
2439
|
+
if (_URL == "") return ""; \
|
|
2440
|
+
definition = target_definition(path); \
|
|
2441
|
+
if (definition != "") path = definition; \
|
|
2442
|
+
else if (path !~ /[\/.]/ || path ~ /[[:space:]]/) return ""; \
|
|
2443
|
+
line = 1; \
|
|
2444
|
+
if (match(path, /:[0-9]+$$/)) { \
|
|
2445
|
+
line = substr(path, RSTART + 1); \
|
|
2446
|
+
path = substr(path, 1, RSTART - 1) \
|
|
2447
|
+
} \
|
|
2448
|
+
if (substr(path, 1, 2) == "./") path = substr(path, 3); \
|
|
2449
|
+
absolute_path = substr(path, 1, 1) == "/" ? path : _CWD "/" path; \
|
|
2450
|
+
if (system("test -f " absolute_path) != 0) return ""; \
|
|
2451
|
+
return "\033]8;;" render_url(_URL, absolute_path, line) "\033\\" \
|
|
2452
|
+
} \
|
|
2453
|
+
{ \
|
|
2454
|
+
while (match($$0, /`[^`]*`/)) { \
|
|
2455
|
+
code_start = RSTART; \
|
|
2456
|
+
code_length = RLENGTH; \
|
|
2457
|
+
inline_code = substr($$0, code_start + 1, code_length - 2); \
|
|
2458
|
+
hyperlink = render_hyperlink(inline_code); \
|
|
2459
|
+
hyperlink_end = hyperlink == "" ? "" : "\033]8;;\033\\"; \
|
|
2460
|
+
$$0 = substr($$0, 1, code_start - 1) hyperlink _OPEN inline_code _CLOSE hyperlink_end substr($$0, code_start + code_length) \
|
|
2461
|
+
} \
|
|
2462
|
+
$1 \
|
|
2463
|
+
}'
|
|
2464
|
+
endef
|
|
2465
|
+
|
|
2466
|
+
#**
|
|
2467
|
+
#* Builds an ANSI escape prefix from a combined color and modifier list.
|
|
2468
|
+
#*
|
|
2469
|
+
#* parameters:
|
|
2470
|
+
#* color_and_or_modifiers: AnyOf<normal | white | black | red | yellow | green | cyan | blue | magenta | bold | underline | reverse | bright>
|
|
2471
|
+
#*
|
|
2472
|
+
#* returns: string
|
|
2473
|
+
#*
|
|
2474
|
+
define _ansi
|
|
2475
|
+
$(call _color \
|
|
2476
|
+
,$(firstword $(filter $(_COLORS),$1)), \
|
|
2477
|
+
$(strip $(filter-out $(firstword $(filter $(_COLORS),$1)),$1)) \
|
|
2478
|
+
)
|
|
2479
|
+
endef
|
|
2480
|
+
|
|
2481
|
+
#**
|
|
2482
|
+
#* The semicolon-separated ANSI codes for a list of modifier tokens.
|
|
2483
|
+
#*
|
|
2484
|
+
#* parameters:
|
|
2485
|
+
#* modifiers: AnyOf<normal | bold | underline | reverse>
|
|
2486
|
+
#*
|
|
2487
|
+
#* returns: string
|
|
2488
|
+
#*
|
|
2489
|
+
define _get_modifiers
|
|
2490
|
+
$(if \
|
|
2491
|
+
$(filter $(MODIFIER_NORMAL),$1),$(_ANSI_MODIFIER_NORMAL),)$(if \
|
|
2492
|
+
$(filter $(MODIFIER_BOLD),$1),$(_ANSI_MODIFIER_BOLD),)$(if \
|
|
2493
|
+
$(filter $(MODIFIER_UNDERLINE),$1),;$(_ANSI_MODIFIER_UNDERLINE),)$(if \
|
|
2494
|
+
$(filter $(MODIFIER_REVERSE),$1),;$(_ANSI_MODIFIER_REVERSE),)
|
|
2495
|
+
endef
|
|
2496
|
+
|
|
2497
|
+
#**
|
|
2498
|
+
#* The ANSI code for a color token, bright when the modifiers ask for it and white when it does not resolve.
|
|
2499
|
+
#*
|
|
2500
|
+
#* parameters:
|
|
2501
|
+
#* color: OneOf<normal | white | black | red | yellow | green | cyan | blue | magenta>
|
|
2502
|
+
#* modifiers?: AnyOf<normal | bold | underline | reverse | bright>
|
|
2503
|
+
#*
|
|
2504
|
+
#* returns: _ANSI_COLOR_*
|
|
2505
|
+
#*
|
|
2506
|
+
define _get_color
|
|
2507
|
+
$(if \
|
|
2508
|
+
$(and $(strip $2),$(or $(if $1,,1),$(filter normal,$1))),$(if \
|
|
2509
|
+
$(filter $(MODIFIER_BRIGHT),$2),$(_ANSI_COLOR_BRIGHT_WHITE),$(_ANSI_COLOR_WHITE)),$(if \
|
|
2510
|
+
$(filter $(MODIFIER_BRIGHT),$2),$(_ANSI_COLOR_BRIGHT_$(_UPPER_$1)),$(_ANSI_COLOR_$(_UPPER_$1))))
|
|
2511
|
+
endef
|
|
2512
|
+
|
|
2513
|
+
#**
|
|
2514
|
+
#* The full ANSI escape sequence for a color and its modifiers.
|
|
2515
|
+
#*
|
|
2516
|
+
#* parameters:
|
|
2517
|
+
#* color: OneOf<normal | white | black | red | yellow | green | cyan | blue | magenta>
|
|
2518
|
+
#* modifiers?: AnyOf<normal | bold | underline | reverse | bright>
|
|
2519
|
+
#*
|
|
2520
|
+
#* returns: string
|
|
2521
|
+
#*
|
|
2522
|
+
define _color
|
|
2523
|
+
$(if $(_IS_NO_ANSI),,\033[$(if $(call _get_modifiers,$2),$(call _get_modifiers,$2);)$(call _get_color,$1,$2)m)
|
|
2524
|
+
endef
|
|
2525
|
+
|
|
2526
|
+
#**
|
|
2527
|
+
#* Converts a string to title case, treating a hyphen as a word break.
|
|
2528
|
+
#*
|
|
2529
|
+
#* parameters:
|
|
2530
|
+
#* text: string
|
|
2531
|
+
#*
|
|
2532
|
+
#* returns: string
|
|
2533
|
+
#*
|
|
2534
|
+
define _to_title_case
|
|
2535
|
+
$(shell $(PRINTF) '%s\n' '$1' | \
|
|
2536
|
+
$(AWK) '{ \
|
|
2537
|
+
gsub(/-/, " "); \
|
|
2538
|
+
for (i = 1; i <= NF; i += 1 ) { \
|
|
2539
|
+
$$i = toupper(substr($$i, 1, 1)) \
|
|
2540
|
+
substr($$i, 2) \
|
|
2541
|
+
} \
|
|
2542
|
+
} \
|
|
2543
|
+
1' \
|
|
2544
|
+
)
|
|
2545
|
+
endef
|
|
2546
|
+
|
|
2547
|
+
#**
|
|
2548
|
+
#* The argument tokens belonging to a target: those before any target, and those following it.
|
|
2549
|
+
#*
|
|
2550
|
+
#* parameters:
|
|
2551
|
+
#* target: string
|
|
2552
|
+
#* goals: list<string>
|
|
2553
|
+
#*
|
|
2554
|
+
#* returns: string
|
|
2555
|
+
#*
|
|
2556
|
+
define _collect_args
|
|
2557
|
+
$(eval _ca_phase := before)$(eval _ca_shared :=)$(eval _ca_own :=)$(foreach \
|
|
2558
|
+
WORD,$(filter-out --,$2),$(if \
|
|
2559
|
+
$(filter before,$(_ca_phase)),$(if \
|
|
2560
|
+
$(filter $(WORD),$(_ALL_TARGETS)),$(if \
|
|
2561
|
+
$(filter $(WORD),$1),$(eval _ca_phase := own),$(eval _ca_phase := skip)),$(eval \
|
|
2562
|
+
_ca_shared += $(subst $$,$$$$,$(WORD)))),$(if \
|
|
2563
|
+
$(filter own,$(_ca_phase)),$(if \
|
|
2564
|
+
$(filter $(WORD),$(_ALL_TARGETS)),$(eval _ca_phase := done),$(eval \
|
|
2565
|
+
_ca_own += $(subst $$,$$$$,$(WORD)))),$(if \
|
|
2566
|
+
$(filter skip,$(_ca_phase)),$(if \
|
|
2567
|
+
$(filter $(WORD),$(_ALL_TARGETS)),$(if \
|
|
2568
|
+
$(filter $(WORD),$1),$(eval _ca_phase := own),))))))$(_ca_own) $(_ca_shared)
|
|
2569
|
+
endef
|
|
2570
|
+
|
|
2571
|
+
#**
|
|
2572
|
+
#* Every UPPER_SNAKE variable as `NAME="value"` shell assignments, escaped and flattened to one line.
|
|
2573
|
+
#*
|
|
2574
|
+
#* returns: string
|
|
2575
|
+
#*
|
|
2576
|
+
define _make_vars_as_env
|
|
2577
|
+
$(foreach VARIABLE,$(shell $(AWK) -v _VAR_NAMES="$(.VARIABLES)" '\
|
|
2578
|
+
BEGIN { \
|
|
2579
|
+
count = split(_VAR_NAMES, names, " "); \
|
|
2580
|
+
for (idx = 1; idx <= count; idx++) \
|
|
2581
|
+
if (names[idx] ~ /^_?[A-Z][A-Z0-9_]*$$/) \
|
|
2582
|
+
print names[idx] \
|
|
2583
|
+
}' \
|
|
2584
|
+
),$(if $(filter simple recursive,$(flavor $(VARIABLE))), \
|
|
2585
|
+
$(VARIABLE)="$(subst $$,\$$,$(subst ",\",$(subst `,\`,$(subst \,\\,$(subst $(_NEWLINE), ,$($(VARIABLE)))))))" \
|
|
2586
|
+
))
|
|
2587
|
+
endef
|
|
2588
|
+
|
|
2589
|
+
#**
|
|
2590
|
+
#* Returns the detected editor name by inspecting well-known terminal environment variables.
|
|
2591
|
+
#*
|
|
2592
|
+
#* returns: OneOf<vscode | phpstorm> | empty-string
|
|
2593
|
+
#*
|
|
2594
|
+
define _editor_from_env
|
|
2595
|
+
$(if $(filter vscode,$(TERM_PROGRAM)),$(EDITOR_VSCODE),$(if \
|
|
2596
|
+
$(VSCODE_PID),$(EDITOR_VSCODE),$(if \
|
|
2597
|
+
$(filter JetBrains-JediTerm,$(TERMINAL_EMULATOR)),$(EDITOR_PHPSTORM),)))
|
|
2598
|
+
endef
|
|
2599
|
+
|
|
2600
|
+
#**
|
|
2601
|
+
#* Returns the detected editor name based on command availability in PATH.
|
|
2602
|
+
#*
|
|
2603
|
+
#* returns: OneOf<vscode | phpstorm> | empty-string
|
|
2604
|
+
#*
|
|
2605
|
+
define _editor_from_command
|
|
2606
|
+
$(if $(shell command -v code >/dev/null 2>&1 && echo 1),$(EDITOR_VSCODE),$(if \
|
|
2607
|
+
$(shell { command -v pstorm || command -v phpstorm; } >/dev/null 2>&1 && echo 1),$(EDITOR_PHPSTORM),))
|
|
2608
|
+
endef
|
|
2609
|
+
|
|
2610
|
+
#---vvv programs
|
|
2611
|
+
|
|
2612
|
+
define _GITATTRIBUTES_PROGRAM_SOURCE
|
|
2613
|
+
$$attributesPath = $$argv[1];
|
|
2614
|
+
$$manifest = json_decode((string) file_get_contents($$argv[2]), true, flags: JSON_THROW_ON_ERROR);
|
|
2615
|
+
|
|
2616
|
+
$$shippedPaths = ['composer.json', 'LICENSE', 'README.md'];
|
|
2617
|
+
$$declaredPaths = array_filter(explode(' ', $$argv[3] ?? ''));
|
|
2618
|
+
|
|
2619
|
+
foreach (['psr-4', 'psr-0', 'classmap', 'files'] as $$section) {
|
|
2620
|
+
foreach ((array) ($$manifest['autoload'][$$section] ?? []) as $$sectionPaths) {
|
|
2621
|
+
foreach ((array) $$sectionPaths as $$sectionPath) {
|
|
2622
|
+
$$shippedPaths[] = $$sectionPath;
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
|
|
2627
|
+
foreach ((array) ($$manifest['bin'] ?? []) as $$binaryPath) {
|
|
2628
|
+
$$shippedPaths[] = $$binaryPath;
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2631
|
+
$$projectDirectory = dirname($$argv[2]);
|
|
2632
|
+
$$expand = static function (array $$paths, bool $$wholeTree) use ($$projectDirectory): array {
|
|
2633
|
+
$$expanded = [];
|
|
2634
|
+
|
|
2635
|
+
foreach ($$paths as $$path) {
|
|
2636
|
+
$$path = (string) preg_replace('#^(?:\./)+#', '', $$path);
|
|
2637
|
+
$$isWholeTree = $$wholeTree
|
|
2638
|
+
? is_dir($$projectDirectory . '/' . trim($$path, '/'))
|
|
2639
|
+
: str_ends_with($$path, '/');
|
|
2640
|
+
$$path = '/' . trim($$path, '/');
|
|
2641
|
+
$$expanded[] = $$path;
|
|
2642
|
+
|
|
2643
|
+
if ($$isWholeTree) {
|
|
2644
|
+
$$expanded[] = $$path . '/**';
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
|
|
2648
|
+
return $$expanded;
|
|
2649
|
+
};
|
|
2650
|
+
|
|
2651
|
+
$$shippedPaths = [...$$expand($$shippedPaths, true), ...$$expand($$declaredPaths, false)];
|
|
2652
|
+
$$shippedLookup = array_flip($$shippedPaths);
|
|
2653
|
+
|
|
2654
|
+
foreach ($$shippedPaths as $$path) {
|
|
2655
|
+
while (($$path = dirname($$path)) !== '/' && $$path !== '.') {
|
|
2656
|
+
$$shippedPaths[] = $$path;
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
$$shippedPaths = array_values(array_unique($$shippedPaths));
|
|
2661
|
+
|
|
2662
|
+
sort($$shippedPaths);
|
|
2663
|
+
|
|
2664
|
+
$$pathWidth = max(array_map(strlen(...), [...$$shippedPaths, '*']));
|
|
2665
|
+
$$renderedLines = [
|
|
2666
|
+
sprintf('%-' . $$pathWidth . 's export-ignore', '*'),
|
|
2667
|
+
sprintf('%-' . $$pathWidth . 's export-ignore', '.*'),
|
|
2668
|
+
];
|
|
2669
|
+
|
|
2670
|
+
foreach ($$shippedPaths as $$path) {
|
|
2671
|
+
$$renderedLines[] = sprintf('%-' . $$pathWidth . 's -export-ignore', $$path)
|
|
2672
|
+
. (isset($$shippedLookup[$$path]) ? '' : ' -export-subst');
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
$$currentContents = (string) file_get_contents($$attributesPath);
|
|
2676
|
+
$$generatedRegion = "###> brnshkr/config ###\n" . implode("\n", $$renderedLines) . "\n###< brnshkr/config ###";
|
|
2677
|
+
$$updatedContents = preg_replace('/###> brnshkr\/config ###.*###< brnshkr\/config ###/s', $$generatedRegion, $$currentContents, 1);
|
|
2678
|
+
|
|
2679
|
+
if ($$updatedContents !== null && $$updatedContents !== $$currentContents) {
|
|
2680
|
+
file_put_contents($$attributesPath, $$updatedContents);
|
|
2681
|
+
echo $$attributesPath;
|
|
2682
|
+
}
|
|
2683
|
+
endef
|
|
2684
|
+
|
|
2685
|
+
define _NAME_INTERNAL_TAG_PROGRAM_SOURCE
|
|
2686
|
+
require $$argv[1];
|
|
2687
|
+
|
|
2688
|
+
$$path = $$argv[2];
|
|
2689
|
+
$$namespace = Brnshkr\Config\ComposerJson::forProjectUsingThisLibrary()->getRootNamespace() ?? 'App';
|
|
2690
|
+
|
|
2691
|
+
file_put_contents($$path, preg_replace('/@internal(?: \S+)?$$/m', '@internal ' . $$namespace, file_get_contents($$path), 1));
|
|
2692
|
+
endef
|
|
2693
|
+
|
|
2694
|
+
#**
|
|
2695
|
+
#* A release body: the file's section where there is one, without its heading and with the compare link.
|
|
2696
|
+
#*
|
|
2697
|
+
#* returns: void
|
|
2698
|
+
#*
|
|
2699
|
+
define _changelog_notes
|
|
2700
|
+
section=$$([ ! -f '$(_CHANGELOG_FILE)' ] || _CHANGELOG_VERSION='$(_CHANGELOG_VERSION)' \
|
|
2701
|
+
$(AWK) "$$_CHANGELOG_SECTION_AWK" '$(_CHANGELOG_FILE)'); \
|
|
2702
|
+
if [ -n "$$section" ]; then \
|
|
2703
|
+
$(CAT) >/dev/null; \
|
|
2704
|
+
$(PRINTF) '%s\n' "$$section"; \
|
|
2705
|
+
else \
|
|
2706
|
+
$(AWK) 'NR == 1 && /^## / { next } { body = body $$0 "\n" } \
|
|
2707
|
+
END { sub(/^\n+/, "", body); printf "%s", body }'; \
|
|
2708
|
+
fi; \
|
|
2709
|
+
[ -z '$(_CHANGELOG_COMPARE)' ] || $(PRINTF) '\n**Full Changelog**: %s\n' '$(_CHANGELOG_COMPARE)'
|
|
2710
|
+
endef
|
|
2711
|
+
|
|
2712
|
+
#**
|
|
2713
|
+
#* Prints the generated changelog under the title, as the file carries it.
|
|
2714
|
+
#*
|
|
2715
|
+
#* returns: void
|
|
2716
|
+
#*
|
|
2717
|
+
define _changelog_print
|
|
2718
|
+
{ $(PRINTF) '%s\n\n' '$(CHANGELOG_TITLE)'; $(CAT); }
|
|
2719
|
+
endef
|
|
2720
|
+
|
|
2721
|
+
#**
|
|
2722
|
+
#* Merges the generated sections into `CHANGELOG_DIR`, keeping every note already written there.
|
|
2723
|
+
#*
|
|
2724
|
+
#* returns: void
|
|
2725
|
+
#*
|
|
2726
|
+
define _changelog_write
|
|
2727
|
+
if [ '$(_CHANGELOG_MAJOR)' = '0' ] \
|
|
2728
|
+
&& ! $(call confirm,`$(_CHANGELOG_VERSION)` is below `1.0.0`$(_COMMA) write it anyway?); then \
|
|
2729
|
+
$(CAT) >/dev/null; \
|
|
2730
|
+
$(call log,Nothing written.,$(COLOR_NOTICE)); \
|
|
2731
|
+
else \
|
|
2732
|
+
$(MKDIR) -p '$(CHANGELOG_DIR)'; \
|
|
2733
|
+
$(CAT) > '$(_CHANGELOG_FILE).added'; \
|
|
2734
|
+
{ \
|
|
2735
|
+
$(AWK) 'NR == 1 { has_title = $$0 == "$(CHANGELOG_TITLE)" } END { exit !has_title }' \
|
|
2736
|
+
'$(_CHANGELOG_FILE)' 2>/dev/null \
|
|
2737
|
+
|| $(PRINTF) '%s\n\n' '$(CHANGELOG_TITLE)'; \
|
|
2738
|
+
_CHANGELOG_ADDED='$(_CHANGELOG_FILE).added' \
|
|
2739
|
+
_CHANGELOG_FORCE='$(filter force f,$(_ARG_WORDS))' \
|
|
2740
|
+
$(AWK) "$$_CHANGELOG_MERGE_AWK" \
|
|
2741
|
+
'$(_CHANGELOG_FILE).added' \
|
|
2742
|
+
$$([ -f '$(_CHANGELOG_FILE)' ] && $(PRINTF) '%s' '$(_CHANGELOG_FILE)'); \
|
|
2743
|
+
} > '$(_CHANGELOG_FILE).new'; \
|
|
2744
|
+
$(RM) -f '$(_CHANGELOG_FILE).added'; \
|
|
2745
|
+
if [ ! -f '$(_CHANGELOG_FILE)' ]; then \
|
|
2746
|
+
$(MV) '$(_CHANGELOG_FILE).new' '$(_CHANGELOG_FILE)' \
|
|
2747
|
+
&& $(call log,Created `%s`.,$(COLOR_SUCCESS),'$(call _named_path,$(_CHANGELOG_FILE))'); \
|
|
2748
|
+
elif $(CMP) -s '$(_CHANGELOG_FILE).new' '$(_CHANGELOG_FILE)'; then \
|
|
2749
|
+
$(RM) -f '$(_CHANGELOG_FILE).new'; \
|
|
2750
|
+
$(call log,Nothing written.,$(COLOR_NOTICE)); \
|
|
2751
|
+
else \
|
|
2752
|
+
$(MV) '$(_CHANGELOG_FILE).new' '$(_CHANGELOG_FILE)' \
|
|
2753
|
+
&& $(call log,Updated `%s`.,$(COLOR_SUCCESS),'$(call _named_path,$(_CHANGELOG_FILE))'); \
|
|
2754
|
+
fi; \
|
|
2755
|
+
fi
|
|
2756
|
+
endef
|
|
2757
|
+
|
|
2758
|
+
define _CHANGELOG_SECTION_AWK_SOURCE
|
|
2759
|
+
function version_of(line, value) {
|
|
2760
|
+
value = line
|
|
2761
|
+
sub(/^## \[?/, "", value)
|
|
2762
|
+
sub(/[]( ].*$$/, "", value)
|
|
2763
|
+
return value
|
|
2764
|
+
}
|
|
2765
|
+
BEGIN { wanted = ENVIRON["_CHANGELOG_VERSION"] }
|
|
2766
|
+
/^## / {
|
|
2767
|
+
inside = (version_of($$0) == wanted)
|
|
2768
|
+
next
|
|
2769
|
+
}
|
|
2770
|
+
inside { body = body $$0 "\n" }
|
|
2771
|
+
END {
|
|
2772
|
+
sub(/^\n+/, "", body)
|
|
2773
|
+
sub(/\n+$$/, "", body)
|
|
2774
|
+
if (body != "") print body
|
|
2775
|
+
}
|
|
2776
|
+
endef
|
|
2777
|
+
|
|
2778
|
+
define _CHANGELOG_MERGE_AWK_SOURCE
|
|
2779
|
+
function pad(value, padded) {
|
|
2780
|
+
padded = "0000000000" value
|
|
2781
|
+
return substr(padded, length(padded) - 9)
|
|
2782
|
+
}
|
|
2783
|
+
function version_of(line, value) {
|
|
2784
|
+
value = line
|
|
2785
|
+
sub(/^## \[?/, "", value)
|
|
2786
|
+
sub(/[]( ].*$$/, "", value)
|
|
2787
|
+
return value
|
|
2788
|
+
}
|
|
2789
|
+
function semver_key(version, value, prerelease, parts, count, position, key, tokens, token_count, token) {
|
|
2790
|
+
value = version
|
|
2791
|
+
sub(/^v/, "", value)
|
|
2792
|
+
sub(/\+.*$$/, "", value)
|
|
2793
|
+
prerelease = ""
|
|
2794
|
+
if (match(value, /-/)) {
|
|
2795
|
+
prerelease = substr(value, RSTART + 1)
|
|
2796
|
+
value = substr(value, 1, RSTART - 1)
|
|
2797
|
+
}
|
|
2798
|
+
count = split(value, parts, ".")
|
|
2799
|
+
key = ""
|
|
2800
|
+
for (position = 1; position <= 3; position += 1) key = key pad((position <= count) ? parts[position] + 0 : 0)
|
|
2801
|
+
if (prerelease == "") return key "1"
|
|
2802
|
+
key = key "0"
|
|
2803
|
+
token_count = split(prerelease, tokens, ".")
|
|
2804
|
+
for (position = 1; position <= token_count; position += 1) {
|
|
2805
|
+
token = tokens[position]
|
|
2806
|
+
key = key ((token ~ /^[0-9]+$$/) ? "0" pad(token + 0) : "1" substr(token "..........", 1, 10))
|
|
2807
|
+
}
|
|
2808
|
+
return key
|
|
2809
|
+
}
|
|
2810
|
+
function hash_of(line, value) {
|
|
2811
|
+
if (!match(line, /\(\[?[0-9a-f]{7}\]?[)(]/)) return ""
|
|
2812
|
+
value = substr(line, RSTART, RLENGTH)
|
|
2813
|
+
gsub(/[^0-9a-f]/, "", value)
|
|
2814
|
+
return value
|
|
2815
|
+
}
|
|
2816
|
+
function is_entry(line) {
|
|
2817
|
+
return (line ~ /^- /) && hash_of(line) != ""
|
|
2818
|
+
}
|
|
2819
|
+
function is_heading(line) {
|
|
2820
|
+
return line ~ /^#{3,4} /
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2823
|
+
function section_text(index_, position, out) {
|
|
2824
|
+
out = ""
|
|
2825
|
+
for (position = 1; position <= added_lines[index_]; position += 1) out = out added_line[index_, position] "\n"
|
|
2826
|
+
sub(/\n+$$/, "", out)
|
|
2827
|
+
return out "\n"
|
|
2828
|
+
}
|
|
2829
|
+
|
|
2830
|
+
function merged_text(existing_index, added_index, position, entry, group, pending, line, out, held, tail, seen_entry, regenerated) {
|
|
2831
|
+
pending = 0
|
|
2832
|
+
for (position = 1; position <= added_entries[added_index]; position += 1) {
|
|
2833
|
+
if ((existing_index, added_entry_hash[added_index, position]) in existing_hash) continue
|
|
2834
|
+
pending += 1
|
|
2835
|
+
pending_group[pending] = added_entry_group[added_index, position]
|
|
2836
|
+
pending_entry[pending] = added_entry[added_index, position]
|
|
2837
|
+
}
|
|
2838
|
+
out = ""
|
|
2839
|
+
group = ""
|
|
2840
|
+
held = ""
|
|
2841
|
+
tail = ""
|
|
2842
|
+
seen_entry = 0
|
|
2843
|
+
for (position = 1; position <= existing_lines[existing_index]; position += 1) {
|
|
2844
|
+
line = existing_line[existing_index, position]
|
|
2845
|
+
if (is_heading(line) || line ~ /^## /) {
|
|
2846
|
+
out = out flush_group(group, held, tail, seen_entry, pending)
|
|
2847
|
+
group = is_heading(line) ? line : ""
|
|
2848
|
+
held = ""
|
|
2849
|
+
tail = ""
|
|
2850
|
+
seen_entry = 0
|
|
2851
|
+
out = out line "\n"
|
|
2852
|
+
continue
|
|
2853
|
+
}
|
|
2854
|
+
if (is_entry(line)) {
|
|
2855
|
+
seen_entry = 1
|
|
2856
|
+
regenerated = ENVIRON["_CHANGELOG_FORCE"] == "" ? "" : added_by_hash[added_index, group, hash_of(line)]
|
|
2857
|
+
held = held tail (regenerated == "" ? line : regenerated) "\n"
|
|
2858
|
+
tail = ""
|
|
2859
|
+
} else if (seen_entry) {
|
|
2860
|
+
tail = tail line "\n"
|
|
2861
|
+
} else {
|
|
2862
|
+
out = out line "\n"
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
out = out flush_group(group, held, tail, seen_entry, pending)
|
|
2866
|
+
for (position = 1; position <= pending; position += 1) {
|
|
2867
|
+
if (pending_used[position]) continue
|
|
2868
|
+
out = out "\n" (pending_group[position] == "" ? "" : pending_group[position] "\n\n") pending_entry[position] "\n"
|
|
2869
|
+
}
|
|
2870
|
+
split("", pending_used)
|
|
2871
|
+
sub(/\n+$$/, "", out)
|
|
2872
|
+
return out "\n"
|
|
2873
|
+
}
|
|
2874
|
+
|
|
2875
|
+
function flush_group(group, held, tail, seen_entry, pending, position, out) {
|
|
2876
|
+
out = held
|
|
2877
|
+
for (position = 1; position <= pending; position += 1) {
|
|
2878
|
+
if (pending_used[position] || pending_group[position] != group) continue
|
|
2879
|
+
out = out pending_entry[position] "\n"
|
|
2880
|
+
pending_used[position] = 1
|
|
2881
|
+
}
|
|
2882
|
+
return out tail
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
# Every section the generator produced, with its entries and the group each sits under.
|
|
2886
|
+
BEGIN { _added = ENVIRON["_CHANGELOG_ADDED"] }
|
|
2887
|
+
FILENAME == _added {
|
|
2888
|
+
if ($$0 ~ /^## /) {
|
|
2889
|
+
added_count += 1
|
|
2890
|
+
added_version[added_count] = version_of($$0)
|
|
2891
|
+
group = ""
|
|
2892
|
+
} else if (is_heading($$0)) {
|
|
2893
|
+
group = $$0
|
|
2894
|
+
}
|
|
2895
|
+
if (added_count == 0) next
|
|
2896
|
+
added_lines[added_count] += 1
|
|
2897
|
+
added_line[added_count, added_lines[added_count]] = $$0
|
|
2898
|
+
if (is_entry($$0)) {
|
|
2899
|
+
added_entries[added_count] += 1
|
|
2900
|
+
added_entry[added_count, added_entries[added_count]] = $$0
|
|
2901
|
+
added_entry_group[added_count, added_entries[added_count]] = group
|
|
2902
|
+
added_entry_hash[added_count, added_entries[added_count]] = hash_of($$0)
|
|
2903
|
+
added_by_hash[added_count, group, hash_of($$0)] = $$0
|
|
2904
|
+
}
|
|
2905
|
+
next
|
|
2906
|
+
}
|
|
2907
|
+
|
|
2908
|
+
# The file as it stands, kept verbatim.
|
|
2909
|
+
{
|
|
2910
|
+
if ($$0 ~ /^## /) {
|
|
2911
|
+
existing_count += 1
|
|
2912
|
+
existing_version[existing_count] = version_of($$0)
|
|
2913
|
+
}
|
|
2914
|
+
if (existing_count == 0) {
|
|
2915
|
+
preamble = preamble $$0 "\n"
|
|
2916
|
+
next
|
|
2917
|
+
}
|
|
2918
|
+
existing_lines[existing_count] += 1
|
|
2919
|
+
existing_line[existing_count, existing_lines[existing_count]] = $$0
|
|
2920
|
+
if (is_entry($$0)) existing_hash[existing_count, hash_of($$0)] = 1
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2923
|
+
# An existing section, with any entry it is missing placed after the last entry of its own group.
|
|
2924
|
+
END {
|
|
2925
|
+
document = preamble
|
|
2926
|
+
|
|
2927
|
+
for (existing_index = 1; existing_index <= existing_count; existing_index += 1) {
|
|
2928
|
+
matched[existing_version[existing_index]] = existing_index
|
|
2929
|
+
}
|
|
2930
|
+
for (added_index = 1; added_index <= added_count; added_index += 1) {
|
|
2931
|
+
matched_added[added_version[added_index]] = added_index
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
# Sections the file does not carry yet, newest first, placed among the ones it does.
|
|
2935
|
+
for (added_index = 1; added_index <= added_count; added_index += 1) {
|
|
2936
|
+
if (added_version[added_index] in matched) continue
|
|
2937
|
+
insert_count += 1
|
|
2938
|
+
insert_key[insert_count] = semver_key(added_version[added_index])
|
|
2939
|
+
insert_index[insert_count] = added_index
|
|
2940
|
+
}
|
|
2941
|
+
for (position = 2; position <= insert_count; position += 1) {
|
|
2942
|
+
key_held = insert_key[position]
|
|
2943
|
+
value_held = insert_index[position]
|
|
2944
|
+
sorted = position - 1
|
|
2945
|
+
while (sorted >= 1 && insert_key[sorted] < key_held) {
|
|
2946
|
+
insert_key[sorted + 1] = insert_key[sorted]
|
|
2947
|
+
insert_index[sorted + 1] = insert_index[sorted]
|
|
2948
|
+
sorted -= 1
|
|
2949
|
+
}
|
|
2950
|
+
insert_key[sorted + 1] = key_held
|
|
2951
|
+
insert_index[sorted + 1] = value_held
|
|
2952
|
+
}
|
|
2953
|
+
|
|
2954
|
+
pending_insert = 1
|
|
2955
|
+
for (existing_index = 1; existing_index <= existing_count; existing_index += 1) {
|
|
2956
|
+
while (pending_insert <= insert_count \
|
|
2957
|
+
&& insert_key[pending_insert] > semver_key(existing_version[existing_index])) {
|
|
2958
|
+
document = document section_text(insert_index[pending_insert]) "\n"
|
|
2959
|
+
pending_insert += 1
|
|
2960
|
+
}
|
|
2961
|
+
replacement = ((existing_version[existing_index] in matched_added) \
|
|
2962
|
+
? merged_text(existing_index, matched_added[existing_version[existing_index]]) : "")
|
|
2963
|
+
if (replacement == "") {
|
|
2964
|
+
for (position = 1; position <= existing_lines[existing_index]; position += 1) {
|
|
2965
|
+
document = document existing_line[existing_index, position] "\n"
|
|
2966
|
+
}
|
|
2967
|
+
} else {
|
|
2968
|
+
document = document replacement
|
|
2969
|
+
}
|
|
2970
|
+
sub(/\n+$$/, "", document)
|
|
2971
|
+
document = document "\n\n"
|
|
2972
|
+
}
|
|
2973
|
+
while (pending_insert <= insert_count) {
|
|
2974
|
+
document = document section_text(insert_index[pending_insert]) "\n"
|
|
2975
|
+
pending_insert += 1
|
|
2976
|
+
}
|
|
2977
|
+
|
|
2978
|
+
sub(/\n+$$/, "", document)
|
|
2979
|
+
print document
|
|
2980
|
+
}
|
|
2981
|
+
endef
|
|
2982
|
+
|
|
2983
|
+
define _CHANGELOG_AWK_SOURCE
|
|
2984
|
+
function sort_pair(keys, values, count, position, sorted, key_held, value_held) {
|
|
2985
|
+
for (position = 2; position <= count; position += 1) {
|
|
2986
|
+
key_held = keys[position]
|
|
2987
|
+
value_held = values[position]
|
|
2988
|
+
sorted = position - 1
|
|
2989
|
+
while (sorted >= 1 && keys[sorted] > key_held) {
|
|
2990
|
+
keys[sorted + 1] = keys[sorted]
|
|
2991
|
+
values[sorted + 1] = values[sorted]
|
|
2992
|
+
sorted -= 1
|
|
2993
|
+
}
|
|
2994
|
+
keys[sorted + 1] = key_held
|
|
2995
|
+
values[sorted + 1] = value_held
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
function readable_scope(scope, parts, count, index_, word, out) {
|
|
2999
|
+
if (scope in names) return names[scope]
|
|
3000
|
+
count = split(scope, parts, /-/)
|
|
3001
|
+
out = ""
|
|
3002
|
+
for (index_ = 1; index_ <= count; index_ += 1) {
|
|
3003
|
+
word = parts[index_]
|
|
3004
|
+
out = out (index_ == 1 ? "" : " ") toupper(substr(word, 1, 1)) substr(word, 2)
|
|
3005
|
+
}
|
|
3006
|
+
return out
|
|
3007
|
+
}
|
|
3008
|
+
function render(section, label, position, inner, seconds, mirrored, keys, entries, second, out) {
|
|
3009
|
+
out = unscoped[section, label]
|
|
3010
|
+
for (position = 1; position <= second_count[section, label]; position += 1) {
|
|
3011
|
+
seconds[position] = second_name[section, label, position]
|
|
3012
|
+
mirrored[position] = second_name[section, label, position]
|
|
3013
|
+
}
|
|
3014
|
+
sort_pair(seconds, mirrored, second_count[section, label])
|
|
3015
|
+
for (position = 1; position <= second_count[section, label]; position += 1) {
|
|
3016
|
+
second = seconds[position]
|
|
3017
|
+
if (out != "") out = out "\n"
|
|
3018
|
+
out = out "#### " readable_scope(second) "\n\n" sub_unscoped[section, label, second]
|
|
3019
|
+
split("", keys)
|
|
3020
|
+
split("", entries)
|
|
3021
|
+
for (inner = 1; inner <= sub_count[section, label, second]; inner += 1) {
|
|
3022
|
+
keys[inner] = sub_key[section, label, second, inner]
|
|
3023
|
+
entries[inner] = sub_entry[section, label, second, inner]
|
|
3024
|
+
}
|
|
3025
|
+
sort_pair(keys, entries, sub_count[section, label, second])
|
|
3026
|
+
for (inner = 1; inner <= sub_count[section, label, second]; inner += 1) out = out entries[inner] "\n"
|
|
3027
|
+
}
|
|
3028
|
+
return out
|
|
3029
|
+
}
|
|
3030
|
+
function major_of(version, value) {
|
|
3031
|
+
value = version
|
|
3032
|
+
sub(/^v/, "", value)
|
|
3033
|
+
sub(/\..*$$/, "", value)
|
|
3034
|
+
return value
|
|
3035
|
+
}
|
|
3036
|
+
function flush(section, version, compare, position, label, printed, out) {
|
|
3037
|
+
if (!(section in has_content)) return ""
|
|
3038
|
+
if (_major != "" && major_of(version) != _major) return ""
|
|
3039
|
+
out = (version == "") ? "" : "## " ((compare != "") ? "[" version "](" compare ")" : version) "\n"
|
|
3040
|
+
if ((section in breaking)) out = out "\n### " breaking_label "\n\n" breaking[section]
|
|
3041
|
+
for (position = 1; position <= rank; position += 1) {
|
|
3042
|
+
label = ordered[position]
|
|
3043
|
+
if (((section, label) in seen) && !(label in printed)) {
|
|
3044
|
+
out = out "\n### " label "\n\n" render(section, label)
|
|
3045
|
+
printed[label] = 1
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
if (((section, other) in seen)) out = out "\n### " other "\n\n" render(section, other)
|
|
3049
|
+
return out
|
|
3050
|
+
}
|
|
3051
|
+
BEGIN {
|
|
3052
|
+
RS = "\x1e"
|
|
3053
|
+
FS = "\x1f"
|
|
3054
|
+
other = "📌 Other"
|
|
3055
|
+
breaking_label = "⚠ Breaking changes"
|
|
3056
|
+
_URL = ENVIRON["_CHANGELOG_URL"]
|
|
3057
|
+
_version = ENVIRON["_CHANGELOG_VERSION"]
|
|
3058
|
+
_compare = ENVIRON["_CHANGELOG_COMPARE"]
|
|
3059
|
+
_all = ENVIRON["_CHANGELOG_ALL"]
|
|
3060
|
+
_commits = ENVIRON["_CHANGELOG_COMMITS"]
|
|
3061
|
+
_types = ENVIRON["_CHANGELOG_TYPES"]
|
|
3062
|
+
_major = ENVIRON["_CHANGELOG_MAJOR"]
|
|
3063
|
+
section = 0
|
|
3064
|
+
section_version[0] = _version
|
|
3065
|
+
pair_count = split(ENVIRON["_CHANGELOG_GROUPS"], pairs, " ")
|
|
3066
|
+
for (index_ = 1; index_ <= pair_count; index_ += 1) {
|
|
3067
|
+
if (pairs[index_] !~ /=/) continue
|
|
3068
|
+
root = pairs[index_]
|
|
3069
|
+
sub(/=.*$$/, "", root)
|
|
3070
|
+
label = pairs[index_]
|
|
3071
|
+
sub(/^[^=]*=/, "", label)
|
|
3072
|
+
gsub(/_/, " ", label)
|
|
3073
|
+
groups[root] = label
|
|
3074
|
+
if (!(label in ranked)) { ranked[label] = ++rank; ordered[rank] = label }
|
|
3075
|
+
}
|
|
3076
|
+
name_count = split(ENVIRON["_CHANGELOG_NAMES"], pairs, " ")
|
|
3077
|
+
for (index_ = 1; index_ <= name_count; index_ += 1) {
|
|
3078
|
+
if (pairs[index_] !~ /=/) continue
|
|
3079
|
+
scope_name = pairs[index_]
|
|
3080
|
+
sub(/=.*$$/, "", scope_name)
|
|
3081
|
+
readable = pairs[index_]
|
|
3082
|
+
sub(/^[^=]*=/, "", readable)
|
|
3083
|
+
gsub(/_/, " ", readable)
|
|
3084
|
+
names[scope_name] = readable
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
{
|
|
3088
|
+
hash = $$1
|
|
3089
|
+
refs = $$2
|
|
3090
|
+
subject = $$3
|
|
3091
|
+
body = $$4
|
|
3092
|
+
sub(/^\n+/, "", hash)
|
|
3093
|
+
|
|
3094
|
+
if (_all != "" && match(refs, /tag: [^,]+/)) {
|
|
3095
|
+
tag = substr(refs, RSTART + 5, RLENGTH - 5)
|
|
3096
|
+
section_compare[section] = (_URL != "") ? _URL "/compare/" tag "..." section_version[section] : ""
|
|
3097
|
+
section += 1
|
|
3098
|
+
section_version[section] = tag
|
|
3099
|
+
}
|
|
3100
|
+
|
|
3101
|
+
if (subject !~ /^[a-z]+(\([^)]+\))?!?: /) next
|
|
3102
|
+
|
|
3103
|
+
scope = ""
|
|
3104
|
+
if (subject ~ /^[a-z]+\(/) {
|
|
3105
|
+
scope = subject
|
|
3106
|
+
sub(/^[a-z]+\(/, "", scope)
|
|
3107
|
+
sub(/\).*$$/, "", scope)
|
|
3108
|
+
}
|
|
3109
|
+
|
|
3110
|
+
text = subject
|
|
3111
|
+
sub(/^[^:]+: /, "", text)
|
|
3112
|
+
is_breaking = (subject ~ /^[a-z]+(\([^)]+\))?!:/) || (body ~ /BREAKING[ -]CHANGE/)
|
|
3113
|
+
type = subject
|
|
3114
|
+
sub(/[(!:].*$$/, "", type)
|
|
3115
|
+
if (!is_breaking && index(" " _types " ", " " type " ") > 0) next
|
|
3116
|
+
|
|
3117
|
+
if (!is_breaking && index(" " _commits " ", " " type "(" scope ")" " ") > 0) next
|
|
3118
|
+
|
|
3119
|
+
root = scope
|
|
3120
|
+
sub(/\/.*$$/, "", root)
|
|
3121
|
+
|
|
3122
|
+
is_grouped = (root in groups)
|
|
3123
|
+
label = is_grouped ? groups[root] : other
|
|
3124
|
+
nested = scope
|
|
3125
|
+
if (is_grouped) sub(/^[^\/]+\/?/, "", nested)
|
|
3126
|
+
second = nested
|
|
3127
|
+
sub(/\/.*$$/, "", second)
|
|
3128
|
+
rest = nested
|
|
3129
|
+
sub(/^[^\/]+\/?/, "", rest)
|
|
3130
|
+
short = substr(hash, 1, 7)
|
|
3131
|
+
reference = (_URL != "") ? " ([" short "](" _URL "/commit/" hash "))" : " (" short ")"
|
|
3132
|
+
|
|
3133
|
+
entry = "- "
|
|
3134
|
+
if (rest != "") entry = entry "**" rest ":** "
|
|
3135
|
+
entry = entry text
|
|
3136
|
+
if (is_breaking) entry = entry " — **breaking**"
|
|
3137
|
+
entry = entry reference
|
|
3138
|
+
|
|
3139
|
+
if (is_breaking) {
|
|
3140
|
+
breaking[section] = breaking[section] "- " (scope != "" ? "**" scope ":** " : "") text \
|
|
3141
|
+
" — **breaking**" reference "\n"
|
|
3142
|
+
}
|
|
3143
|
+
|
|
3144
|
+
if (nested == "") {
|
|
3145
|
+
unscoped[section, label] = unscoped[section, label] entry "\n"
|
|
3146
|
+
} else {
|
|
3147
|
+
if (!((section, label, second) in second_seen)) {
|
|
3148
|
+
second_seen[section, label, second] = 1
|
|
3149
|
+
second_count[section, label] += 1
|
|
3150
|
+
second_name[section, label, second_count[section, label]] = second
|
|
3151
|
+
}
|
|
3152
|
+
if (rest == "") {
|
|
3153
|
+
sub_unscoped[section, label, second] = sub_unscoped[section, label, second] entry "\n"
|
|
3154
|
+
} else {
|
|
3155
|
+
sub_count[section, label, second] += 1
|
|
3156
|
+
sub_key[section, label, second, sub_count[section, label, second]] = rest
|
|
3157
|
+
sub_entry[section, label, second, sub_count[section, label, second]] = entry
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
|
|
3161
|
+
seen[section, label] = 1
|
|
3162
|
+
has_content[section] = 1
|
|
3163
|
+
}
|
|
3164
|
+
END {
|
|
3165
|
+
for (index_ = 0; index_ <= section; index_ += 1) {
|
|
3166
|
+
rendered = flush(index_, section_version[index_], (_all != "") ? section_compare[index_] : _compare)
|
|
3167
|
+
if (rendered == "") continue
|
|
3168
|
+
document = (document == "") ? rendered : document "\n" rendered
|
|
3169
|
+
}
|
|
3170
|
+
if (document == "") exit
|
|
3171
|
+
sub(/\n+$$/, "", document)
|
|
3172
|
+
print document
|
|
3173
|
+
}
|
|
3174
|
+
endef
|
|
3175
|
+
|
|
3176
|
+
define _GROUP_AWK_SOURCE
|
|
3177
|
+
function repeat(text, times, out) { out = ""; while (times-- > 0) out = out text; return out }
|
|
3178
|
+
function value(record, key, pattern) {
|
|
3179
|
+
pattern = "\"" key "\"[ \t]*:[ \t]*\""
|
|
3180
|
+
if (!match(record, pattern)) return ""
|
|
3181
|
+
record = substr(record, RSTART + RLENGTH)
|
|
3182
|
+
if (!match(record, /^([^"\\]|\\.)*/)) return ""
|
|
3183
|
+
return substr(record, RSTART, RLENGTH)
|
|
3184
|
+
}
|
|
3185
|
+
function unescape(text) {
|
|
3186
|
+
gsub(/\\"/, "\"", text)
|
|
3187
|
+
gsub(/\\\//, "/", text)
|
|
3188
|
+
gsub(/\\\\/, "\\", text)
|
|
3189
|
+
return text
|
|
3190
|
+
}
|
|
3191
|
+
function add(identifier, message) {
|
|
3192
|
+
if (identifier == "" || identifier == "null") return
|
|
3193
|
+
identifier = unescape(identifier)
|
|
3194
|
+
message = unescape(message)
|
|
3195
|
+
if (!(identifier in seen)) { seen[identifier] = 1; order[++total] = identifier }
|
|
3196
|
+
count[identifier] += 1
|
|
3197
|
+
if (example[identifier] == "") example[identifier] = message
|
|
3198
|
+
}
|
|
3199
|
+
BEGIN { if (_IDENTIFIER != "" && _MESSAGE != "") RS = "}" }
|
|
3200
|
+
{ raw = raw (NR > 1 ? RS : "") $$0; if ($$0 ~ /(^|\n)[ \t]*[[{]/) has_structured_output = 1 }
|
|
3201
|
+
_IDENTIFIER == "" && match($$0, /(\([0-9]+,[0-9]+\):|:[0-9]+(:[0-9]+)?) error [A-Za-z0-9_.\/-]+:? /) { token = substr($$0, RSTART, RLENGTH); sub(/^[^ ]* error /, "", token); sub(/:? $$/, "", token); add(token, substr($$0, RSTART + RLENGTH)) }
|
|
3202
|
+
_IDENTIFIER != "" && _MESSAGE != "" {
|
|
3203
|
+
pending = pending $$0
|
|
3204
|
+
quotes = pending
|
|
3205
|
+
gsub(/\\./, "", quotes)
|
|
3206
|
+
if (gsub(/"/, "", quotes) % 2) { pending = pending "}"; next }
|
|
3207
|
+
if (index(pending, "\"suppressedMessages\"")) is_in_suppressed_messages = 1
|
|
3208
|
+
if (!is_in_suppressed_messages) add(value(pending, _IDENTIFIER), value(pending, _MESSAGE))
|
|
3209
|
+
if (index(pending, "\"errorCount\"")) is_in_suppressed_messages = 0
|
|
3210
|
+
pending = ""
|
|
3211
|
+
}
|
|
3212
|
+
_IDENTIFIER != "" && _MESSAGE == "" { buffer = buffer " " $$0 }
|
|
3213
|
+
END {
|
|
3214
|
+
pattern = "\"" _IDENTIFIER "\":[ \t]*\\["
|
|
3215
|
+
rest = buffer
|
|
3216
|
+
while (_IDENTIFIER != "" && _MESSAGE == "" && match(rest, pattern)) {
|
|
3217
|
+
items = substr(rest, RSTART + RLENGTH)
|
|
3218
|
+
rest = items
|
|
3219
|
+
sub(/\].*/, "", items)
|
|
3220
|
+
size = split(items, parts, ",")
|
|
3221
|
+
for (item = 1; item <= size; item += 1) {
|
|
3222
|
+
gsub(/^[ \t]*"|"[ \t]*$$/, "", parts[item])
|
|
3223
|
+
add(parts[item], "")
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
for (outer = 1; outer < total; outer += 1)
|
|
3227
|
+
for (inner = 1; inner <= total - outer; inner += 1)
|
|
3228
|
+
if (count[order[inner]] < count[order[inner + 1]]) {
|
|
3229
|
+
swap = order[inner]; order[inner] = order[inner + 1]; order[inner + 1] = swap
|
|
3230
|
+
}
|
|
3231
|
+
padding = repeat(" ", 2 * _INDENT)
|
|
3232
|
+
if (total == 0 && (_STATUS != 0 || (_IDENTIFIER != "" && !has_structured_output))) {
|
|
3233
|
+
sub(/\n+$$/, "", raw)
|
|
3234
|
+
gsub(/\n/, "\n" padding, raw)
|
|
3235
|
+
if (raw ~ /[^ \t\n]/) printf "%s%s\n", padding, raw
|
|
3236
|
+
exit _STATUS
|
|
3237
|
+
}
|
|
3238
|
+
if (total == 0) { printf "%s$(call text,✔,$(COLOR_SUCCESS)) $(call text,No findings,$(COLOR_DESCRIPTION))\n", padding; exit }
|
|
3239
|
+
for (position = 1; position <= total; position += 1) {
|
|
3240
|
+
if (length(order[position]) > width) width = length(order[position])
|
|
3241
|
+
if (length(count[order[position]]) > number_width) number_width = length(count[order[position]])
|
|
3242
|
+
findings += count[order[position]]
|
|
3243
|
+
}
|
|
3244
|
+
for (position = 1; position <= total; position += 1) {
|
|
3245
|
+
identifier = order[position]
|
|
3246
|
+
message = example[identifier]
|
|
3247
|
+
printf "%s%s$(call text,%s,$(COLOR_ENTRY)) $(call text,%s,$(COLOR_ERROR))%s", \
|
|
3248
|
+
padding, repeat(" ", number_width - length(count[identifier])), count[identifier], identifier, \
|
|
3249
|
+
(message == "" ? "\n" : repeat(" ", width - length(identifier)) " ")
|
|
3250
|
+
if (message != "") printf "$(call text,%s,$(COLOR_DESCRIPTION))\n", message
|
|
3251
|
+
}
|
|
3252
|
+
printf "%s$(call text,✘,$(COLOR_ERROR)) $(call text,%s %s,$(COLOR_DESCRIPTION))\n", padding, findings, (findings == 1 ? "finding" : "findings")
|
|
3253
|
+
}
|
|
3254
|
+
endef
|
|
3255
|
+
|
|
3256
|
+
define _HELP_AWK_SOURCE
|
|
3257
|
+
function trim(string) { gsub(/^[ \t\r\n]+|[ \t\r\n]+$$/, "", string); return string }
|
|
3258
|
+
function leaf(path) { sub(/.*\//, "", path); return path }
|
|
3259
|
+
function repeat(text, count, out) { out = ""; while (count-- > 0) out = out text; return out }
|
|
3260
|
+
function character_bytes(leading_byte) {
|
|
3261
|
+
return (leading_byte < "\200") ? 1 : ((leading_byte < "\340") ? 2 : ((leading_byte < "\360") ? 3 : 4))
|
|
3262
|
+
}
|
|
3263
|
+
function character_width(character) {
|
|
3264
|
+
if (character < "\340") return 1
|
|
3265
|
+
if (character < "\360") return (character >= "\342\230\200" && character <= "\342\237\277") ? 2 : 1
|
|
3266
|
+
return 2
|
|
3267
|
+
}
|
|
3268
|
+
function display_width(text, position, byte_count, total_width) {
|
|
3269
|
+
total_width = 0
|
|
3270
|
+
for (position = 1; position <= length(text); position += byte_count) {
|
|
3271
|
+
byte_count = character_bytes(substr(text, position, 1))
|
|
3272
|
+
total_width += character_width(substr(text, position, byte_count))
|
|
3273
|
+
}
|
|
3274
|
+
return total_width
|
|
3275
|
+
}
|
|
3276
|
+
function truncate(text, width_limit, position, byte_count, character, taken_width, truncated_text) {
|
|
3277
|
+
if (display_width(text) <= width_limit) return text
|
|
3278
|
+
taken_width = 0
|
|
3279
|
+
truncated_text = ""
|
|
3280
|
+
for (position = 1; position <= length(text); position += byte_count) {
|
|
3281
|
+
byte_count = character_bytes(substr(text, position, 1))
|
|
3282
|
+
character = substr(text, position, byte_count)
|
|
3283
|
+
if (taken_width + character_width(character) > width_limit - 1) break
|
|
3284
|
+
truncated_text = truncated_text character
|
|
3285
|
+
taken_width += character_width(character)
|
|
3286
|
+
}
|
|
3287
|
+
return truncated_text "…"
|
|
3288
|
+
}
|
|
3289
|
+
function render_url(template, file, line) {
|
|
3290
|
+
if (template == "") return ""
|
|
3291
|
+
gsub(/\{cwd\}/, ENVIRON["_CURDIR"], template)
|
|
3292
|
+
gsub(/\{wslDistro\}/, ENVIRON["WSL_DISTRO_NAME"], template)
|
|
3293
|
+
gsub(/\{file\}/, file, template)
|
|
3294
|
+
gsub(/\{line\}/, line, template)
|
|
3295
|
+
return template
|
|
3296
|
+
}
|
|
3297
|
+
function strip_verbosity(text) { sub(/[ \t]*#v+([ \t]|$$)/, "", text); return text }
|
|
3298
|
+
function line_verbosity(line, verbosity_match) { if (line == "") line = $$0; if (!match(line, /#v+([[:space:]]|$$)/)) return 0; verbosity_match = substr(line, RSTART + 1, RLENGTH - 1); sub(/[[:space:]]$$/, "", verbosity_match); if (length(verbosity_match) > max_verbosity) max_verbosity = length(verbosity_match); return length(verbosity_match) }
|
|
3299
|
+
function get_description() { if (!match($$0, /#~~[ ]*/)) return ""; return trim(strip_verbosity(substr($$0, RSTART + RLENGTH))) }
|
|
3300
|
+
function render_inline_code(text, out, ansi_open, ansi_close) {
|
|
3301
|
+
ansi_open = ENVIRON["_INLINE_CODE_OPEN"]
|
|
3302
|
+
ansi_close = ENVIRON["_INLINE_CODE_CLOSE"]
|
|
3303
|
+
out = ""
|
|
3304
|
+
while (match(text, /`[^`]+`/)) {
|
|
3305
|
+
out = out substr(text, 1, RSTART - 1) ansi_open substr(text, RSTART + 1, RLENGTH - 2) ansi_close
|
|
3306
|
+
text = substr(text, RSTART + RLENGTH)
|
|
3307
|
+
}
|
|
3308
|
+
return out text
|
|
3309
|
+
}
|
|
3310
|
+
function derive_description(documentation, line) {
|
|
3311
|
+
match(documentation, /^[^\n]+/)
|
|
3312
|
+
line = tolower(substr(documentation, RSTART, 1)) substr(documentation, RSTART + 1, RLENGTH - 1)
|
|
3313
|
+
sub(/\.[[:space:]]*$$/, "", line)
|
|
3314
|
+
return line
|
|
3315
|
+
}
|
|
3316
|
+
function expand_make(expr, result, varname, previous) {
|
|
3317
|
+
result = expr
|
|
3318
|
+
while (match(result, /\$$[({][A-Za-z0-9_]+[)}]/)) {
|
|
3319
|
+
previous = result
|
|
3320
|
+
varname = substr(result, RSTART + 2, RLENGTH - 3)
|
|
3321
|
+
result = substr(result, 1, RSTART - 1) (varname in ENVIRON ? ENVIRON[varname] : "") substr(result, RSTART + RLENGTH)
|
|
3322
|
+
if (result == previous) break
|
|
3323
|
+
}
|
|
3324
|
+
return result
|
|
3325
|
+
}
|
|
3326
|
+
function does_path_exist(path, line, does_exist) { does_exist = (getline line < path) >= 0; close(path); return does_exist }
|
|
3327
|
+
function eval_wildcard(line, count, paths, i, path) {
|
|
3328
|
+
count = split(substr(line, index(line, "wildcard") + 9), paths, /[[:space:]]+/)
|
|
3329
|
+
for (i = 1; i <= count; i += 1) {
|
|
3330
|
+
path = paths[i]
|
|
3331
|
+
if (!index(path, "/")) continue
|
|
3332
|
+
path = substr(path, index(path, "/"))
|
|
3333
|
+
sub(/[^A-Za-z0-9_.\/-]+$$/, "", path)
|
|
3334
|
+
if (does_path_exist(ENVIRON["_CURDIR"] path)) return 1
|
|
3335
|
+
}
|
|
3336
|
+
return 0
|
|
3337
|
+
}
|
|
3338
|
+
function eval_condition(line, varname) {
|
|
3339
|
+
if (line ~ /^ifneq/ && index(line, "$$(wildcard ")) return eval_wildcard(line)
|
|
3340
|
+
if (match(line, /^ifdef[ \t]+/)) {
|
|
3341
|
+
varname = trim(substr(line, RLENGTH + 1))
|
|
3342
|
+
return (varname in ENVIRON && ENVIRON[varname] != "")
|
|
3343
|
+
}
|
|
3344
|
+
if (line ~ /^ifndef[ \t]+/) return 1
|
|
3345
|
+
return eval_ifeq(line)
|
|
3346
|
+
}
|
|
3347
|
+
function eval_ifeq(line, op_eq, rest, depth, comma, i, c, arg1, arg2, quote) {
|
|
3348
|
+
if (!match(line, /^ifn?eq[ \t]*/)) return 1
|
|
3349
|
+
op_eq = (substr(line, 1, 4) == "ifeq")
|
|
3350
|
+
rest = substr(line, RLENGTH + 1)
|
|
3351
|
+
if (substr(rest, 1, 1) == "\"" || substr(rest, 1, 1) == SQ) {
|
|
3352
|
+
quote = substr(rest, 1, 1)
|
|
3353
|
+
if (!match(rest, "^" quote "[^" quote "]*" quote "[ \t]+" quote "[^" quote "]*" quote)) return 1
|
|
3354
|
+
i = index(substr(rest, 2), quote) + 1
|
|
3355
|
+
arg1 = expand_make(substr(rest, 2, i - 2))
|
|
3356
|
+
rest = substr(rest, i + 1)
|
|
3357
|
+
sub(/^[ \t]+/, "", rest)
|
|
3358
|
+
i = index(substr(rest, 2), substr(rest, 1, 1)) + 1
|
|
3359
|
+
arg2 = expand_make(substr(rest, 2, i - 2))
|
|
3360
|
+
return op_eq ? (arg1 == arg2) : (arg1 != arg2)
|
|
3361
|
+
}
|
|
3362
|
+
if (substr(rest, 1, 1) != "(") return 1
|
|
3363
|
+
for (i = 2; i <= length(rest); i++) {
|
|
3364
|
+
c = substr(rest, i, 1)
|
|
3365
|
+
if (c == "(") depth++
|
|
3366
|
+
else if (c == ")" && depth > 0) depth--
|
|
3367
|
+
else if (c == ")") break
|
|
3368
|
+
else if (c == "," && depth == 0 && !comma) comma = i
|
|
3369
|
+
}
|
|
3370
|
+
arg1 = expand_make(trim(substr(rest, 2, comma - 2)))
|
|
3371
|
+
arg2 = expand_make(trim(substr(rest, comma + 1, i - comma - 1)))
|
|
3372
|
+
return op_eq ? (arg1 == arg2) : (arg1 != arg2)
|
|
3373
|
+
}
|
|
3374
|
+
function begin_condition(line) {
|
|
3375
|
+
condition_depth++
|
|
3376
|
+
if (line ~ /^ifndef[ \t]/) is_in_ifndef = 1
|
|
3377
|
+
is_parent_active = (condition_depth == 1 || condition_is_active[condition_depth - 1])
|
|
3378
|
+
condition_is_active[condition_depth] = is_parent_active ? eval_condition(line) : 0
|
|
3379
|
+
condition_branch_matched[condition_depth] = condition_is_active[condition_depth]
|
|
3380
|
+
}
|
|
3381
|
+
function else_condition(line, else_rest) {
|
|
3382
|
+
if (condition_depth == 0) return
|
|
3383
|
+
is_parent_active = (condition_depth == 1 || condition_is_active[condition_depth - 1])
|
|
3384
|
+
else_rest = line; sub(/^else[ \t]*/, "", else_rest)
|
|
3385
|
+
if (is_parent_active && else_rest ~ /^(ifeq|ifneq|ifdef|ifndef)[ \t]/) {
|
|
3386
|
+
if (condition_branch_matched[condition_depth]) condition_is_active[condition_depth] = 0
|
|
3387
|
+
else {
|
|
3388
|
+
condition_is_active[condition_depth] = eval_condition(else_rest)
|
|
3389
|
+
if (condition_is_active[condition_depth]) condition_branch_matched[condition_depth] = 1
|
|
3390
|
+
}
|
|
3391
|
+
} else if (is_parent_active) condition_is_active[condition_depth] = !condition_branch_matched[condition_depth]
|
|
3392
|
+
}
|
|
3393
|
+
function matches_scope(path, i, f) {
|
|
3394
|
+
if (scope_filter_count == 0) return 1
|
|
3395
|
+
if (path == "main") return 1
|
|
3396
|
+
for (i = 1; i <= scope_filter_count; i += 1) {
|
|
3397
|
+
f = scope_filter[i]
|
|
3398
|
+
if (f == "") continue
|
|
3399
|
+
if (path == f) return 1
|
|
3400
|
+
if (index(path, f "/") == 1) return 1
|
|
3401
|
+
if (index(f, path "/") == 1) return 1
|
|
3402
|
+
}
|
|
3403
|
+
return 0
|
|
3404
|
+
}
|
|
3405
|
+
function is_scope_target(path, i, f) {
|
|
3406
|
+
if (scope_filter_count == 0) return 1
|
|
3407
|
+
for (i = 1; i <= scope_filter_count; i += 1) {
|
|
3408
|
+
f = scope_filter[i]
|
|
3409
|
+
if (f == "") continue
|
|
3410
|
+
if (path == f) return 1
|
|
3411
|
+
if (index(path, f "/") == 1) return 1
|
|
3412
|
+
}
|
|
3413
|
+
return 0
|
|
3414
|
+
}
|
|
3415
|
+
function is_scope_visible(path) { return scope_verbosity[path] + 0 <= verbosity }
|
|
3416
|
+
function scope_has_visible_entries(scope_path, section, idx) {
|
|
3417
|
+
if (!matches_scope(scope_path)) return 0
|
|
3418
|
+
if (!is_scope_target(scope_path)) return 0
|
|
3419
|
+
if (!is_scope_visible(scope_path)) return 0
|
|
3420
|
+
for (idx = 1; idx <= section_count[scope_path, section] + 0; idx += 1)
|
|
3421
|
+
if (section_verbosity[scope_path, section, idx] + 0 <= verbosity) return 1
|
|
3422
|
+
return 0
|
|
3423
|
+
}
|
|
3424
|
+
function push_scope(level, name, verbosity, current, parent_verbosity, parent) {
|
|
3425
|
+
scope_at[level] = (level > 0 && scope_at[level - 1] != "" ? scope_at[level - 1] "/" name : name)
|
|
3426
|
+
scope_depth = level
|
|
3427
|
+
current = scope_at[level]
|
|
3428
|
+
parent_verbosity = (level > 0 ? scope_verbosity[scope_at[level - 1]] + 0 : 0)
|
|
3429
|
+
scope_verbosity[current] = (verbosity > parent_verbosity ? verbosity : parent_verbosity)
|
|
3430
|
+
if (current in seen_scope) return
|
|
3431
|
+
seen_scope[current] = 1
|
|
3432
|
+
parent = (level > 0 ? scope_at[level - 1] : "")
|
|
3433
|
+
if (parent == "" || parent == "main") {
|
|
3434
|
+
if (parent == "main" && !("main" in seen_scope)) { seen_scope["main"] = 1; top_count += 1; top_order[top_count] = "main" }
|
|
3435
|
+
top_count += 1
|
|
3436
|
+
top_order[top_count] = current
|
|
3437
|
+
} else {
|
|
3438
|
+
child_count[parent] += 1
|
|
3439
|
+
children[parent, child_count[parent]] = current
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
function alias_of(name, map, position, remainder) {
|
|
3443
|
+
map = " " ENVIRON["_TARGET_ALIAS_MAP"] " "
|
|
3444
|
+
position = index(map, " " name "=")
|
|
3445
|
+
if (!position) return ""
|
|
3446
|
+
remainder = substr(map, position + length(name) + 2)
|
|
3447
|
+
return ", " substr(remainder, 1, index(remainder, " ") - 1)
|
|
3448
|
+
}
|
|
3449
|
+
function add_dotenv_variables( keys, key_count, key_idx, name) {
|
|
3450
|
+
key_count = split(ENVIRON["_DOTENV_FILE_KEYS"], keys, /[[:space:]]+/)
|
|
3451
|
+
scope_depth = 0
|
|
3452
|
+
scope_at[0] = "main"
|
|
3453
|
+
for (key_idx = 1; key_idx <= key_count; key_idx += 1) {
|
|
3454
|
+
name = keys[key_idx]
|
|
3455
|
+
if (name == "" || name in scraped_variable || !(name in ENVIRON)) continue
|
|
3456
|
+
add("variables", name, ENVIRON[name], "?=", "", 0, 0)
|
|
3457
|
+
section_file["main", "variables", section_count["main", "variables"]] = ""
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
function resolve_scope( current) {
|
|
3461
|
+
current = (scope_depth > 0 ? scope_at[scope_depth] : scope_at[0] != "" ? scope_at[0] : "main")
|
|
3462
|
+
if (current == "main" && !("main" in seen_scope)) { seen_scope["main"] = 1; top_count += 1; top_order[top_count] = "main" }
|
|
3463
|
+
return current
|
|
3464
|
+
}
|
|
3465
|
+
function add(section, name, text, extra, description, line, entry_verbosity, current, idx) {
|
|
3466
|
+
current = resolve_scope()
|
|
3467
|
+
if (!current) return
|
|
3468
|
+
if (name != "" && seen_symbol[current, section, name]++) return
|
|
3469
|
+
idx = ++section_count[current, section]
|
|
3470
|
+
section_name[current, section, idx] = name (section == "commands" ? alias_of(name) : "")
|
|
3471
|
+
section_text[current, section, idx] = text
|
|
3472
|
+
section_extra[current, section, idx] = extra
|
|
3473
|
+
section_description[current, section, idx] = description
|
|
3474
|
+
section_file[current, section, idx] = FILENAME
|
|
3475
|
+
section_line[current, section, idx] = line
|
|
3476
|
+
section_verbosity[current, section, idx] = (entry_verbosity != "" ? entry_verbosity : line_verbosity())
|
|
3477
|
+
}
|
|
3478
|
+
function handle_define(documentation, description) {
|
|
3479
|
+
if ($$2 ~ /^_/) return
|
|
3480
|
+
if ($$2 ~ /^[A-Z][A-Z0-9_]*$$/) {
|
|
3481
|
+
add("variables", $$2, "(macro)", (is_in_ifndef ? "?=" : ":="), get_description(), FNR)
|
|
3482
|
+
scraped_variable[$$2] = 1
|
|
3483
|
+
} else {
|
|
3484
|
+
description = get_description()
|
|
3485
|
+
if (documentation != "") add("functions", $$2, documentation, (description != "" ? description : derive_description(documentation)), "", FNR)
|
|
3486
|
+
else add("functions", $$2, "", description, "", FNR)
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
function has_content(scope_path, section, idx) {
|
|
3490
|
+
if (!matches_scope(scope_path)) return 0
|
|
3491
|
+
if (scope_has_visible_entries(scope_path, section)) return 1
|
|
3492
|
+
for (idx = 1; idx <= child_count[scope_path]; idx += 1)
|
|
3493
|
+
if (has_content(children[scope_path, idx], section)) return 1
|
|
3494
|
+
return 0
|
|
3495
|
+
}
|
|
3496
|
+
function has_any_content(scope_path) {
|
|
3497
|
+
return has_content(scope_path, "description") || has_content(scope_path, "variables") \
|
|
3498
|
+
|| has_content(scope_path, "functions") || has_content(scope_path, "commands")
|
|
3499
|
+
}
|
|
3500
|
+
function lowest_verbosity(scope_path, shown, level) {
|
|
3501
|
+
shown = verbosity
|
|
3502
|
+
for (level = 0; level <= max_verbosity; level += 1) { verbosity = level; if (has_any_content(scope_path)) break }
|
|
3503
|
+
verbosity = shown
|
|
3504
|
+
return (level <= max_verbosity ? level : -1)
|
|
3505
|
+
}
|
|
3506
|
+
function tree_max(scope_path, depth, section, mode, best, idx, width, child_width, value) {
|
|
3507
|
+
if (!matches_scope(scope_path)) return 0
|
|
3508
|
+
if (is_scope_visible(scope_path) && is_scope_target(scope_path)) {
|
|
3509
|
+
for (idx = 1; idx <= section_count[scope_path, section] + 0; idx += 1) {
|
|
3510
|
+
if (section_verbosity[scope_path, section, idx] + 0 > verbosity) continue
|
|
3511
|
+
if (mode == "value" && section_description[scope_path, section, idx] == "") continue
|
|
3512
|
+
value = section_text[scope_path, section, idx]
|
|
3513
|
+
if (mode == "value" && ENVIRON["_HAS_RESOLVE_ARGUMENT"] != "" && section_name[scope_path, section, idx] in ENVIRON) value = trim(ENVIRON[section_name[scope_path, section, idx]])
|
|
3514
|
+
width = (mode == "value") ? length(value) : (depth + 2) * ENVIRON["INDENT"] + length(section_name[scope_path, section, idx])
|
|
3515
|
+
if (width > best) best = width
|
|
3516
|
+
}
|
|
3517
|
+
}
|
|
3518
|
+
for (idx = 1; idx <= child_count[scope_path]; idx += 1) {
|
|
3519
|
+
child_width = tree_max(children[scope_path, idx], depth + 1, section, mode)
|
|
3520
|
+
if (child_width > best) best = child_width
|
|
3521
|
+
}
|
|
3522
|
+
return best
|
|
3523
|
+
}
|
|
3524
|
+
function walk(scope_path, depth, section, column, render_scope, padding, width, format, idx, entry, text, extra, description, count, lines, line_idx, has_pending_blank, was_function_printed) {
|
|
3525
|
+
if (!matches_scope(scope_path)) return
|
|
3526
|
+
if (!has_content(scope_path, section)) return
|
|
3527
|
+
if (scope_path != "main") {
|
|
3528
|
+
printf "%s$(call text,%s,$(COLOR_SCOPE))\n", repeat(" ", (depth + 1) * ENVIRON["INDENT"]), leaf(scope_path)
|
|
3529
|
+
}
|
|
3530
|
+
render_scope = scope_has_visible_entries(scope_path, section)
|
|
3531
|
+
if (render_scope) {
|
|
3532
|
+
padding = repeat(" ", (depth + 2) * ENVIRON["INDENT"])
|
|
3533
|
+
width = column - (depth + 2) * ENVIRON["INDENT"]
|
|
3534
|
+
for (idx = 1; idx <= section_count[scope_path, section] + 0; idx += 1) {
|
|
3535
|
+
if (section_verbosity[scope_path, section, idx] + 0 > verbosity) continue
|
|
3536
|
+
entry = section_name[scope_path, section, idx]
|
|
3537
|
+
text = section_text[scope_path, section, idx]
|
|
3538
|
+
extra = section_extra[scope_path, section, idx]
|
|
3539
|
+
description = section_description[scope_path, section, idx]
|
|
3540
|
+
if (section == "variables" && ENVIRON["_HAS_RESOLVE_ARGUMENT"] != "" && entry in ENVIRON) text = trim(ENVIRON[entry])
|
|
3541
|
+
if (section == "variables") description = render_inline_code(description)
|
|
3542
|
+
else if (section == "commands") text = render_inline_code(text)
|
|
3543
|
+
else if (section == "functions") { extra = render_inline_code(extra); text = render_inline_code(text) }
|
|
3544
|
+
else if (section == "description") text = render_inline_code(text)
|
|
3545
|
+
file = section_file[scope_path, section, idx]
|
|
3546
|
+
line = section_line[scope_path, section, idx]
|
|
3547
|
+
if (file != "" && file !~ /^\//) file = ENVIRON["_CURDIR"] "/" file
|
|
3548
|
+
if (ENVIRON["_WORKDIR"] != ENVIRON["_CURDIR"] && index(file, ENVIRON["_WORKDIR"] "/") == 1) \
|
|
3549
|
+
file = ENVIRON["_CURDIR"] substr(file, length(ENVIRON["_WORKDIR"]) + 1)
|
|
3550
|
+
url = (file != "" ? render_url(ENVIRON["_EDITOR_URL"], file, line) : "")
|
|
3551
|
+
hyperlink = ""
|
|
3552
|
+
hyperlink_end = ""
|
|
3553
|
+
if (url != "" && !ENVIRON["_IS_NO_ANSI"]) {
|
|
3554
|
+
hyperlink = "\033]8;;" url "\033\\"
|
|
3555
|
+
hyperlink_end = "\033]8;;\033\\"
|
|
3556
|
+
}
|
|
3557
|
+
entry_linked = hyperlink entry hyperlink_end
|
|
3558
|
+
entry_pad = width - length(entry)
|
|
3559
|
+
if (entry_pad < 0) entry_pad = 0
|
|
3560
|
+
entry_padded = entry_linked repeat(" ", entry_pad)
|
|
3561
|
+
if (section == "variables") {
|
|
3562
|
+
if (description != "") {
|
|
3563
|
+
text = truncate(text, description_column)
|
|
3564
|
+
format = sprintf("%2s %s%s", extra, text, repeat(" ", description_column - display_width(text)))
|
|
3565
|
+
printf "%s$(call text,%s,$(COLOR_ENTRY)) $(call text,%s (%s),$(COLOR_DESCRIPTION))\n", padding, entry_padded, format, description
|
|
3566
|
+
} else {
|
|
3567
|
+
printf "%s$(call text,%s,$(COLOR_ENTRY)) $(call text,%2s %s,$(COLOR_DESCRIPTION))\n", padding, entry_padded, extra, text
|
|
3568
|
+
}
|
|
3569
|
+
} else if (section == "commands") {
|
|
3570
|
+
printf "%s$(call text,%s,$(COLOR_ENTRY)) $(call text,%s,$(COLOR_DESCRIPTION))\n", padding, entry_padded, text
|
|
3571
|
+
} else if (section == "functions") {
|
|
3572
|
+
if (was_function_printed && verbosity >= 2 && text != "") printf "\n"
|
|
3573
|
+
was_function_printed = (text != "" && verbosity >= 2)
|
|
3574
|
+
if (extra != "" && (verbosity < 2 || text == "")) {
|
|
3575
|
+
printf "%s$(call text,%s,$(COLOR_ENTRY)) $(call text,%s,$(COLOR_DESCRIPTION))\n", padding, entry_padded, extra
|
|
3576
|
+
} else {
|
|
3577
|
+
printf "%s$(call text,%s,$(COLOR_ENTRY))\n", padding, entry_linked
|
|
3578
|
+
}
|
|
3579
|
+
if (verbosity >= 2 && text != "") {
|
|
3580
|
+
count = split(text, lines, "\n")
|
|
3581
|
+
has_pending_blank = 0
|
|
3582
|
+
for (line_idx = 1; line_idx <= count; line_idx += 1) {
|
|
3583
|
+
if (lines[line_idx] == "") { if (verbosity < 3) break; has_pending_blank = 1; continue }
|
|
3584
|
+
if (has_pending_blank) { printf "\n"; has_pending_blank = 0 }
|
|
3585
|
+
printf "%s$(call text,%s,$(COLOR_DESCRIPTION))\n", padding " ", lines[line_idx]
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
} else {
|
|
3589
|
+
printf "%s$(call text,%s,$(COLOR_DESCRIPTION))\n", padding, text
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
for (idx = 1; idx <= child_count[scope_path]; idx += 1) {
|
|
3594
|
+
if (!has_content(children[scope_path, idx], section)) continue
|
|
3595
|
+
walk(children[scope_path, idx], depth + 1, section, column)
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3598
|
+
function print_section(has_previous, section, label, column, top_idx, top_path, found, needs_separator) {
|
|
3599
|
+
for (top_idx = 1; top_idx <= top_count; top_idx += 1) {
|
|
3600
|
+
top_path = top_order[top_idx]
|
|
3601
|
+
if (!has_content(top_path, section)) continue
|
|
3602
|
+
if (!found++) { if (has_previous) printf "\n"; printf "$(call text,%s,$(COLOR_SECTION_LABEL))\n", label }
|
|
3603
|
+
else if (needs_separator) printf "\n"
|
|
3604
|
+
walk(top_path, 0, section, column)
|
|
3605
|
+
needs_separator = (section == "functions" && verbosity >= 2)
|
|
3606
|
+
}
|
|
3607
|
+
return found + 0
|
|
3608
|
+
}
|
|
3609
|
+
BEGIN { SQ = sprintf("%c", 39); verbosity = ENVIRON["_VERBOSITY"] + 0; max_verbosity = verbosity; seen_scope["main"] = 1; top_count = 1; top_order[1] = "main"; scope_filter_count = (ENVIRON["_SCOPES"] != "" ? split(ENVIRON["_SCOPES"], scope_filter, /[[:space:]]+/) : 0); name_count = split(ENVIRON["_TARGET_NAMES"], name_pairs, /[[:space:]]+/); for (i = 1; i <= name_count; i += 1) { at = index(name_pairs[i], "="); resolved_name[substr(name_pairs[i], 1, at - 1)] = substr(name_pairs[i], at + 1) } }
|
|
3610
|
+
current_file != FILENAME { current_file = FILENAME; for (i in scope_at) delete scope_at[i]; for (i in condition_is_active) delete condition_is_active[i]; for (i in condition_branch_matched) delete condition_branch_matched[i]; scope_depth = 0; is_in_define = 0; is_in_documentation = 0; is_in_ifndef = 0; condition_depth = 0 }
|
|
3611
|
+
/^endef([[:space:]].*)?$$/ { is_in_define = 0; next }
|
|
3612
|
+
is_in_define { next }
|
|
3613
|
+
/^(ifeq|ifneq|ifdef|ifndef)[[:space:]]/ { begin_condition($$0); next }
|
|
3614
|
+
/^else([[:space:]]|$$)/ { else_condition($$0); next }
|
|
3615
|
+
/^endif([[:space:]]|$$)/ { if (condition_depth > 0) { condition_depth--; is_in_ifndef = 0 } next }
|
|
3616
|
+
condition_depth > 0 && !condition_is_active[condition_depth] { next }
|
|
3617
|
+
/^#-+/ { match($$0, /^#-+/); scope_dash_count = RLENGTH - 1; scope_suffix = substr($$0, RLENGTH + 1); match(scope_suffix, /^v*/); scope_verbosity_count = RLENGTH; push_scope(scope_dash_count - 2, trim(substr(scope_suffix, scope_verbosity_count + 1)), scope_verbosity_count); next }
|
|
3618
|
+
/^#\*\*/ { is_in_documentation = 1; documentation_text = ""; next }
|
|
3619
|
+
is_in_documentation {
|
|
3620
|
+
if (/^#\*/) { sub(/^#\*[ ]?/, ""); documentation_text = documentation_text $$0 "\n"; next }
|
|
3621
|
+
if (/^define[ \t]+/) { sub(/\n+$$/, "", documentation_text); handle_define(documentation_text); is_in_documentation = 0; is_in_define = 1; next }
|
|
3622
|
+
if (!/^#/) { is_in_documentation = 0; next }
|
|
3623
|
+
}
|
|
3624
|
+
/^define[[:space:]]+/ { handle_define(""); is_in_define = 1; next }
|
|
3625
|
+
/^#!!/ { sub(/^#!![ ]*/, ""); add("description", "", strip_verbosity($$0), "", "", FNR); next }
|
|
3626
|
+
/^[A-Za-z0-9_]+[ \t]*[?:+!]*=/ {
|
|
3627
|
+
line_number = FNR
|
|
3628
|
+
var_source = $$0
|
|
3629
|
+
var_description = ""
|
|
3630
|
+
var_verbosity = ""
|
|
3631
|
+
if (match(var_source, /#~~[ ]*/)) {
|
|
3632
|
+
var_description = trim(strip_verbosity(substr(var_source, RSTART + RLENGTH)))
|
|
3633
|
+
var_source = substr(var_source, 1, RSTART - 1)
|
|
3634
|
+
}
|
|
3635
|
+
match(var_source, /^[A-Za-z0-9_]+/)
|
|
3636
|
+
var_name = substr(var_source, RSTART, RLENGTH)
|
|
3637
|
+
var_rest = substr(var_source, RSTART + RLENGTH)
|
|
3638
|
+
match(var_rest, /^[ \t]*[?:+!]*=/)
|
|
3639
|
+
var_operator = substr(var_rest, RSTART, RLENGTH)
|
|
3640
|
+
gsub(/[ \t]/, "", var_operator)
|
|
3641
|
+
if (var_name !~ /^_/) {
|
|
3642
|
+
raw = substr(var_source, index(var_source, "=") + 1)
|
|
3643
|
+
while (raw ~ /\\[ \t]*$$/) {
|
|
3644
|
+
sub(/[ \t]*\\[ \t]*$$/, "", raw)
|
|
3645
|
+
if ((getline continuation) <= 0) break
|
|
3646
|
+
sub(/^[ \t]+/, "", continuation)
|
|
3647
|
+
if (var_verbosity == "") {
|
|
3648
|
+
continuation_verbosity = line_verbosity(continuation)
|
|
3649
|
+
if (continuation_verbosity) var_verbosity = continuation_verbosity
|
|
3650
|
+
}
|
|
3651
|
+
if (match(continuation, /#~~[ ]*/)) {
|
|
3652
|
+
if (var_description == "")
|
|
3653
|
+
var_description = trim(strip_verbosity(substr(continuation, RSTART + RLENGTH)))
|
|
3654
|
+
continuation = substr(continuation, 1, RSTART - 1)
|
|
3655
|
+
sub(/[ \t]*\\[ \t]*$$/, "", continuation)
|
|
3656
|
+
if (continuation != "") raw = raw " " continuation
|
|
3657
|
+
break
|
|
3658
|
+
}
|
|
3659
|
+
raw = raw " " continuation
|
|
3660
|
+
}
|
|
3661
|
+
add("variables", var_name, trim(strip_verbosity(raw)), (is_in_ifndef ? "?=" : var_operator), var_description, line_number, var_verbosity)
|
|
3662
|
+
scraped_variable[var_name] = 1
|
|
3663
|
+
}
|
|
3664
|
+
next
|
|
3665
|
+
}
|
|
3666
|
+
function target_name(name) { return (name in resolved_name) ? resolved_name[name] : ENVIRON["_TARGET_PREFIX"] name }
|
|
3667
|
+
{ while (match($$0, /\$$\(call _target,[a-zA-Z0-9_.-]+\)/)) $$0 = substr($$0, 1, RSTART - 1) target_name(substr($$0, RSTART + 15, RLENGTH - 16)) substr($$0, RSTART + RLENGTH) }
|
|
3668
|
+
{ gsub(/\$$\(_TARGET_PREFIX\)/, ENVIRON["_TARGET_PREFIX"]) }
|
|
3669
|
+
|
|
3670
|
+
/^[A-Za-z0-9_.-]+([ \t]+[A-Za-z0-9_.-]+)*:([^=]|$$)/ {
|
|
3671
|
+
split($$0, parts, ":")
|
|
3672
|
+
description = get_description()
|
|
3673
|
+
count = split(parts[1], names, /[ \t]+/)
|
|
3674
|
+
joined_commands = ""
|
|
3675
|
+
for (i = 1; i <= count; i += 1) {
|
|
3676
|
+
name = names[i]
|
|
3677
|
+
if (name == "" || name ~ /^[_\.]/) continue
|
|
3678
|
+
if (name in command_seen) continue
|
|
3679
|
+
command_seen[name] = 1
|
|
3680
|
+
joined_commands = (joined_commands == "" ? name : joined_commands "/" name)
|
|
3681
|
+
}
|
|
3682
|
+
if (joined_commands != "") add("commands", joined_commands, description, "", "", FNR)
|
|
3683
|
+
next
|
|
3684
|
+
}
|
|
3685
|
+
END {
|
|
3686
|
+
if (ENVIRON["_EDITOR_UNKNOWN"] != "") {
|
|
3687
|
+
editor_count = split(ENVIRON["_EDITORS"], editor_names, " ")
|
|
3688
|
+
editor_choices = ""
|
|
3689
|
+
for (editor_idx = 1; editor_idx <= editor_count; editor_idx += 1)
|
|
3690
|
+
editor_choices = editor_choices (editor_idx == 1 ? "" : "|") editor_names[editor_idx]
|
|
3691
|
+
printf "$(call text,[$(_LABEL)],$(COLOR_ERROR)) Unknown editor \"$(call text,%s,$(COLOR_WARNING))\".\n", ENVIRON["_EDITOR_UNKNOWN"]
|
|
3692
|
+
printf "$(call text,[$(_LABEL)],$(COLOR_NOTICE)) Run with \"$(call text,EDITOR=<%s>,$(MODIFIER_UNDERLINE))\" or \"$(call text,EDITOR=,$(MODIFIER_UNDERLINE))\" to disable.\n", editor_choices
|
|
3693
|
+
exit $(BRNSHKR_CONFIG_ERROR_CODE)
|
|
3694
|
+
}
|
|
3695
|
+
for (scope_path in seen_scope)
|
|
3696
|
+
if (!index(scope_path, "/") && (scope_path ~ /^v+$$/ || index(" " ENVIRON["_HELP_WORDS"] " ", " " scope_path " "))) {
|
|
3697
|
+
printf "$(call text,[$(_LABEL)],$(COLOR_ERROR)) Scope \"$(call text,%s,$(COLOR_WARNING))\" takes a name that help reserves.\n", scope_path
|
|
3698
|
+
printf "$(call text,[$(_LABEL)],$(COLOR_NOTICE)) Rename it; help reads \"$(call text,%s,$(MODIFIER_UNDERLINE))\" as a word of its own.\n", scope_path
|
|
3699
|
+
exit $(BRNSHKR_CONFIG_ERROR_CODE)
|
|
3700
|
+
}
|
|
3701
|
+
unknown_filter_count = 0
|
|
3702
|
+
for (filter_idx = 1; filter_idx <= scope_filter_count; filter_idx += 1) {
|
|
3703
|
+
filter = scope_filter[filter_idx]
|
|
3704
|
+
if (filter == "") continue
|
|
3705
|
+
filter_verbosity = lowest_verbosity(filter)
|
|
3706
|
+
if (filter_verbosity >= 0 && filter_verbosity <= verbosity) continue
|
|
3707
|
+
filter_display = filter; gsub(/\//, ".", filter_display)
|
|
3708
|
+
if (filter_verbosity < 0) unknown_filters[++unknown_filter_count] = filter_display
|
|
3709
|
+
else { printf "$(call text,[$(_LABEL)],$(COLOR_ERROR)) Scope \"$(call text,%s,$(COLOR_WARNING))\" shows nothing below %s.\n", filter_display, repeat("v", filter_verbosity); is_hidden = 1 }
|
|
3710
|
+
}
|
|
3711
|
+
if (is_hidden && !unknown_filter_count) exit $(BRNSHKR_CONFIG_ERROR_CODE)
|
|
3712
|
+
if (unknown_filter_count > 0) {
|
|
3713
|
+
unknown_message = ""
|
|
3714
|
+
for (filter_idx = 1; filter_idx <= unknown_filter_count; filter_idx += 1) {
|
|
3715
|
+
separator = (filter_idx == 1 ? "" : (filter_idx == unknown_filter_count ? " and " : ", "))
|
|
3716
|
+
unknown_message = unknown_message separator sprintf("\"$(call text,%s,$(COLOR_WARNING))\"", unknown_filters[filter_idx])
|
|
3717
|
+
}
|
|
3718
|
+
unknown_label = (unknown_filter_count == 1 ? "scope" : "scopes")
|
|
3719
|
+
printf "$(call text,[$(_LABEL)],$(COLOR_ERROR)) Unknown %s %s.\n", unknown_label, unknown_message
|
|
3720
|
+
printf "$(call text,[$(_LABEL)],$(COLOR_NOTICE)) Run \"$(call text,make help list-scopes,$(MODIFIER_UNDERLINE))\" or \"$(call text,make help ls,$(MODIFIER_UNDERLINE))\" to see available scopes.\n"
|
|
3721
|
+
exit $(BRNSHKR_CONFIG_ERROR_CODE)
|
|
3722
|
+
}
|
|
3723
|
+
if (ENVIRON["_HAS_LIST_SCOPES_ARGUMENT"]) {
|
|
3724
|
+
listed_count = 0
|
|
3725
|
+
for (scope_path in seen_scope) {
|
|
3726
|
+
if (scope_path == "main") continue
|
|
3727
|
+
if (!matches_scope(scope_path)) continue
|
|
3728
|
+
if (!is_scope_target(scope_path) && scope_filter_count > 0) continue
|
|
3729
|
+
if (!has_any_content(scope_path)) continue
|
|
3730
|
+
listed_paths[++listed_count] = scope_path
|
|
3731
|
+
}
|
|
3732
|
+
for (sort_idx = 2; sort_idx <= listed_count; sort_idx += 1) {
|
|
3733
|
+
current_path = listed_paths[sort_idx]; compare_idx = sort_idx - 1
|
|
3734
|
+
while (compare_idx >= 1 && listed_paths[compare_idx] > current_path) {
|
|
3735
|
+
listed_paths[compare_idx + 1] = listed_paths[compare_idx]
|
|
3736
|
+
compare_idx -= 1
|
|
3737
|
+
}
|
|
3738
|
+
listed_paths[compare_idx + 1] = current_path
|
|
3739
|
+
}
|
|
3740
|
+
for (listed_idx = 1; listed_idx <= listed_count; listed_idx += 1) {
|
|
3741
|
+
gsub(/\//, ".", listed_paths[listed_idx])
|
|
3742
|
+
print listed_paths[listed_idx]
|
|
3743
|
+
}
|
|
3744
|
+
exit 0
|
|
3745
|
+
}
|
|
3746
|
+
if (ENVIRON["_HAS_RESOLVE_ARGUMENT"] != "") add_dotenv_variables()
|
|
3747
|
+
logo = ENVIRON["_HEADER_LOGO"]; sub(/__BRNSHKR_LOGO_END__$$/, "", logo); logo_reset = ""
|
|
3748
|
+
if (match(logo, /\033\[[0-9;]*m$$/)) { logo_reset = substr(logo, RSTART); logo = substr(logo, 1, RSTART - 1) }
|
|
3749
|
+
if (length(trim(logo)) > 0) printf "%s%s\n", logo, logo_reset
|
|
3750
|
+
if (length(trim(ENVIRON["_HEADER_TITLE"])) > 0) printf "%s\n\n", ENVIRON["_HEADER_TITLE"]
|
|
3751
|
+
printf "%s\n", ENVIRON["_HEADER_USAGE"]
|
|
3752
|
+
for (top_idx = 1; top_idx <= top_count; top_idx += 1) {
|
|
3753
|
+
top_path = top_order[top_idx]
|
|
3754
|
+
column = tree_max(top_path, 0, "variables", "name"); if (column > variable_column) variable_column = column
|
|
3755
|
+
column = tree_max(top_path, 0, "variables", "value"); if (column > description_column) description_column = column
|
|
3756
|
+
if (description_column > ENVIRON["VALUE_WIDTH"] + 0) description_column = ENVIRON["VALUE_WIDTH"] + 0
|
|
3757
|
+
column = tree_max(top_path, 0, "functions", "name"); if (column > function_column) function_column = column
|
|
3758
|
+
column = tree_max(top_path, 0, "commands", "name"); if (column > command_column) command_column = column
|
|
3759
|
+
}
|
|
3760
|
+
has_printed = 1
|
|
3761
|
+
has_printed += print_section(has_printed, "description", "Description:", 0)
|
|
3762
|
+
has_printed += print_section(has_printed, "variables", "Variables:", variable_column)
|
|
3763
|
+
has_printed += print_section(has_printed, "functions", "Functions:", function_column)
|
|
3764
|
+
has_printed += print_section(has_printed, "commands", "Available commands:", command_column)
|
|
3765
|
+
}
|
|
3766
|
+
endef
|
|
3767
|
+
|
|
3768
|
+
export EDITOR_URL
|
|
3769
|
+
|
|
3770
|
+
$(call _target,cc): _ARG_VALUES = $(_CACHES)
|
|
3771
|
+
$(call _target,changelog): export _CHANGELOG_AWK = $(_CHANGELOG_AWK_SOURCE)
|
|
3772
|
+
$(call _target,changelog): export _CHANGELOG_MERGE_AWK = $(_CHANGELOG_MERGE_AWK_SOURCE)
|
|
3773
|
+
$(call _target,changelog): export _CHANGELOG_SECTION_AWK = $(_CHANGELOG_SECTION_AWK_SOURCE)
|
|
3774
|
+
$(call _target,configs): export _GITATTRIBUTES_PROGRAM = $(_GITATTRIBUTES_PROGRAM_SOURCE)
|
|
3775
|
+
$(call _target,configs): export _NAME_INTERNAL_TAG_PROGRAM = $(_NAME_INTERNAL_TAG_PROGRAM_SOURCE)
|
|
3776
|
+
$(call _target,configs): _ARG_VALUES = $(_CONFIG_NAMES) local l force f
|
|
3777
|
+
$(call _target,help): export _HELP_AWK = $(_HELP_AWK_SOURCE)
|
|
3778
|
+
$(_GROUPS): export _GROUP_AWK = $(_GROUP_AWK_SOURCE)
|