2018-03-28 13:22:52 +02:00
PKG := github.com/lightningnetwork/lnd
2018-03-29 07:16:18 +02:00
ESCPKG := github.com\/ lightningnetwork\/ lnd
2019-01-24 14:51:06 +01:00
MOBILE_PKG := $( PKG) /mobile
2022-02-07 13:58:22 +01:00
TOOLS_DIR := tools
2018-03-29 07:16:18 +02:00
2018-06-05 03:35:45 +02:00
BTCD_PKG := github.com/btcsuite/btcd
2019-01-18 12:17:58 +01:00
GOACC_PKG := github.com/ory/go-acc
2022-02-07 13:58:20 +01:00
GOIMPORTS_PKG := github.com/rinchsan/gosimports/cmd/gosimports
2018-03-29 07:16:18 +02:00
GO_BIN := ${ GOPATH } /bin
BTCD_BIN := $( GO_BIN) /btcd
2022-02-07 13:58:22 +01:00
GOIMPORTS_BIN := $( GO_BIN) /gosimports
2023-01-27 21:38:38 +01:00
GOMOBILE_BIN := $( GO_BIN) /gomobile
2019-01-18 12:17:58 +01:00
GOACC_BIN := $( GO_BIN) /go-acc
2018-03-29 07:16:18 +02:00
2019-01-24 14:51:06 +01:00
MOBILE_BUILD_DIR := ${ GOPATH } /src/$( MOBILE_PKG) /build
IOS_BUILD_DIR := $( MOBILE_BUILD_DIR) /ios
2021-10-07 18:48:36 +02:00
IOS_BUILD := $( IOS_BUILD_DIR) /Lndmobile.xcframework
2019-01-24 14:51:06 +01:00
ANDROID_BUILD_DIR := $( MOBILE_BUILD_DIR) /android
ANDROID_BUILD := $( ANDROID_BUILD_DIR) /Lndmobile.aar
2018-03-29 07:16:18 +02:00
2020-12-21 13:03:14 +01:00
COMMIT := $( shell git describe --tags --dirty)
2018-01-29 17:11:03 +01:00
2021-10-01 21:45:39 +02:00
GOBUILD := go build -v
GOINSTALL := go install -v
2022-05-11 19:18:17 +02:00
GOTEST := go test
2018-03-29 07:16:18 +02:00
2021-11-29 13:11:04 +01:00
GOFILES_NOVENDOR = $( shell find . -type f -name '*.go' -not -path "./vendor/*" -not -name "*pb.go" -not -name "*pb.gw.go" -not -name "*.pb.json.go" )
2018-03-29 07:16:18 +02:00
2018-08-10 08:17:07 +02:00
RM := rm -f
CP := cp
MAKE := make
2018-10-26 02:10:49 +02:00
XARGS := xargs -L 1
2018-08-10 08:17:07 +02:00
i n c l u d e m a k e / t e s t i n g _ f l a g s . m k
2020-04-21 10:19:58 +02:00
i n c l u d e m a k e / r e l e a s e _ f l a g s . m k
2020-09-23 14:55:08 +02:00
i n c l u d e m a k e / f u z z _ f l a g s . m k
2018-08-10 08:17:07 +02:00
2018-09-26 13:31:40 +02:00
DEV_TAGS := $( if ${ tags } ,$( DEV_TAGS) ${ tags } ,$( DEV_TAGS) )
2020-04-21 10:19:58 +02:00
# We only return the part inside the double quote here to avoid escape issues
# when calling the external release script. The second parameter can be used to
# add additional ldflags if needed (currently only used for the release).
2022-10-02 00:09:34 +02:00
make_ldflags = $( 1) -X $( PKG) /build.Commit= $( COMMIT)
2020-04-10 02:04:55 +02:00
2021-12-18 22:52:47 +01:00
DEV_GCFLAGS := -gcflags "all=-N -l"
2022-10-02 00:09:34 +02:00
DEV_LDFLAGS := -ldflags " $( call make_ldflags) "
2020-04-21 10:19:58 +02:00
# For the release, we want to remove the symbol table and debug information (-s)
# and omit the DWARF symbol table (-w). Also we clear the build ID.
2022-10-02 00:09:34 +02:00
RELEASE_LDFLAGS := $( call make_ldflags, -s -w -buildid= )
2020-04-10 02:04:55 +02:00
2020-03-28 10:39:17 +01:00
# Linting uses a lot of memory, so keep it under control by limiting the number
# of workers if requested.
i f n e q ( $( workers ) , )
LINT_WORKERS = --concurrency= $( workers)
e n d i f
2023-04-18 22:20:14 +02:00
DOCKER_TOOLS = docker run --rm -v $$ ( pwd ) :/build lnd-tools
2018-03-29 07:16:18 +02:00
GREEN := "\\033[0;32m"
NC := "\\033[0m"
d e f i n e p r i n t
echo $( GREEN) $1 $( NC)
e n d e f
default : scratch
all : scratch check install
# ============
# DEPENDENCIES
# ============
2019-01-18 12:17:58 +01:00
$(GOACC_BIN) :
2021-11-29 13:11:04 +01:00
@$( call print, "Installing go-acc." )
2022-02-07 13:58:22 +01:00
cd $( TOOLS_DIR) ; go install -trimpath -tags= tools $( GOACC_PKG)
2019-01-18 12:17:58 +01:00
2022-02-07 13:58:22 +01:00
$(BTCD_BIN) :
2018-12-01 00:20:50 +01:00
@$( call print, "Installing btcd." )
2022-02-07 13:58:22 +01:00
cd $( TOOLS_DIR) ; go install -trimpath $( BTCD_PKG)
2018-01-29 17:11:03 +01:00
2022-02-07 13:58:22 +01:00
$(GOIMPORTS_BIN) :
2020-07-15 16:36:59 +02:00
@$( call print, "Installing goimports." )
2022-02-07 13:58:22 +01:00
cd $( TOOLS_DIR) ; go install -trimpath $( GOIMPORTS_PKG)
2020-07-15 16:36:59 +02:00
2018-03-29 07:16:18 +02:00
# ============
# INSTALLATION
# ============
2018-01-29 17:11:03 +01:00
2018-03-29 07:16:18 +02:00
build :
2018-04-29 13:49:14 +02:00
@$( call print, "Building debug lnd and lncli." )
2021-12-18 22:52:47 +01:00
$( GOBUILD) -tags= " $( DEV_TAGS) " -o lnd-debug $( DEV_GCFLAGS) $( DEV_LDFLAGS) $( PKG) /cmd/lnd
$( GOBUILD) -tags= " $( DEV_TAGS) " -o lncli-debug $( DEV_GCFLAGS) $( DEV_LDFLAGS) $( PKG) /cmd/lncli
2018-03-28 13:22:52 +02:00
2018-11-02 09:49:32 +01:00
build-itest :
2020-12-03 11:30:27 +01:00
@$( call print, "Building itest btcd and lnd." )
2022-08-12 09:16:35 +02:00
CGO_ENABLED = 0 $( GOBUILD) -tags= "integration" -o itest/btcd-itest$( EXEC_SUFFIX) $( DEV_LDFLAGS) $( BTCD_PKG)
2022-08-11 13:39:40 +02:00
CGO_ENABLED = 0 $( GOBUILD) -tags= " $( ITEST_TAGS) " -o itest/lnd-itest$( EXEC_SUFFIX) $( DEV_LDFLAGS) $( PKG) /cmd/lnd
2020-12-03 11:30:27 +01:00
@$( call print, " Building itest binary for ${ backend } backend. " )
2022-08-12 09:16:35 +02:00
CGO_ENABLED = 0 $( GOTEST) -v ./itest -tags= " $( DEV_TAGS) $( RPC_TAGS) integration $( backend) " -c -o itest/itest.test$( EXEC_SUFFIX)
2020-07-31 10:32:30 +02:00
2021-07-19 18:11:21 +02:00
build-itest-race :
@$( call print, "Building itest btcd and lnd with race detector." )
2022-08-12 09:16:35 +02:00
CGO_ENABLED = 0 $( GOBUILD) -tags= "integration" -o itest/btcd-itest$( EXEC_SUFFIX) $( DEV_LDFLAGS) $( BTCD_PKG)
2022-08-11 13:39:40 +02:00
CGO_ENABLED = 1 $( GOBUILD) -race -tags= " $( ITEST_TAGS) " -o itest/lnd-itest$( EXEC_SUFFIX) $( DEV_LDFLAGS) $( PKG) /cmd/lnd
2021-07-19 18:11:21 +02:00
@$( call print, " Building itest binary for ${ backend } backend. " )
2022-08-12 09:16:35 +02:00
CGO_ENABLED = 0 $( GOTEST) -v ./itest -tags= " $( DEV_TAGS) $( RPC_TAGS) integration $( backend) " -c -o itest/itest.test$( EXEC_SUFFIX)
2021-07-19 18:11:21 +02:00
2018-03-28 13:22:52 +02:00
install :
2018-03-29 07:16:18 +02:00
@$( call print, "Installing lnd and lncli." )
2022-10-17 18:46:09 +02:00
$( GOINSTALL) -tags= " ${ tags } " -ldflags= " $( RELEASE_LDFLAGS) " $( PKG) /cmd/lnd
$( GOINSTALL) -tags= " ${ tags } " -ldflags= " $( RELEASE_LDFLAGS) " $( PKG) /cmd/lncli
2018-03-28 13:22:52 +02:00
2021-01-13 14:26:29 +01:00
release-install :
@$( call print, "Installing release lnd and lncli." )
env CGO_ENABLED = 0 $( GOINSTALL) -v -trimpath -ldflags= " $( RELEASE_LDFLAGS) " -tags= " $( RELEASE_TAGS) " $( PKG) /cmd/lnd
env CGO_ENABLED = 0 $( GOINSTALL) -v -trimpath -ldflags= " $( RELEASE_LDFLAGS) " -tags= " $( RELEASE_TAGS) " $( PKG) /cmd/lncli
2021-02-05 13:11:35 +01:00
# Make sure the generated mobile RPC stubs don't influence our vendor package
# by removing them first in the clean-mobile target.
release : clean -mobile
2020-04-21 10:19:58 +02:00
@$( call print, "Releasing lnd and lncli binaries." )
$( VERSION_CHECK)
2020-04-24 14:27:48 +02:00
./scripts/release.sh build-release " $( VERSION_TAG) " " $( BUILD_SYSTEM) " " $( RELEASE_TAGS) " " $( RELEASE_LDFLAGS) "
2020-04-21 10:19:58 +02:00
2021-02-17 16:58:29 +01:00
docker-release :
2021-01-08 10:40:17 +01:00
@$( call print, "Building release helper docker image." )
if [ " $( tag) " = "" ] ; then echo "Must specify tag=<commit_or_tag>!" ; exit 1; fi
docker build -t lnd-release-helper -f make/builder.Dockerfile make/
2021-02-17 16:58:29 +01:00
# Run the actual compilation inside the docker image. We pass in all flags
# that we might want to overwrite in manual tests.
2022-10-02 00:09:34 +02:00
$( DOCKER_RELEASE_HELPER) make release tag = " $( tag) " sys = " $( sys) " COMMIT = " $( COMMIT) "
2021-01-08 10:40:17 +01:00
2022-02-10 16:01:59 +01:00
docker-tools :
@$( call print, "Building tools docker image." )
docker build -q -t lnd-tools $( TOOLS_DIR)
2018-11-29 02:17:38 +01:00
scratch : build
2018-03-28 13:22:52 +02:00
2018-03-29 07:16:18 +02:00
# =======
2018-03-28 13:22:52 +02:00
# TESTING
2018-03-29 07:16:18 +02:00
# =======
2018-03-28 13:22:52 +02:00
2018-03-29 07:16:18 +02:00
check : unit itest
2018-03-28 13:22:52 +02:00
2021-06-14 16:24:02 +02:00
db-instance :
i f e q ( $( dbbackend ) , p o s t g r e s )
# Remove a previous postgres instance if it exists.
docker rm lnd-postgres --force || echo "Starting new postgres container"
2021-12-27 09:36:51 +01:00
# Start a fresh postgres instance. Allow a maximum of 500 connections so
# that multiple lnd instances with a maximum number of connections of 50
# each can run concurrently.
docker run --name lnd-postgres -e POSTGRES_PASSWORD = postgres -p 6432:5432 -d postgres:13-alpine -N 500
2021-10-01 10:25:51 +02:00
docker logs -f lnd-postgres &
2021-06-14 16:24:02 +02:00
# Wait for the instance to be started.
2021-10-01 10:25:51 +02:00
sleep $( POSTGRES_START_DELAY)
2021-06-14 16:24:02 +02:00
e n d i f
itest-only : db -instance
2019-05-24 14:17:48 +02:00
@$( call print, " Running integration tests with ${ backend } backend. " )
2022-08-11 13:39:40 +02:00
rm -rf itest/*.log itest/.logs-*; date
2020-12-03 11:30:26 +01:00
EXEC_SUFFIX = $( EXEC_SUFFIX) scripts/itest_part.sh 0 1 $( TEST_FLAGS) $( ITEST_FLAGS)
2018-03-28 13:22:52 +02:00
2020-12-03 11:30:27 +01:00
itest : build -itest itest -only
2020-11-04 11:03:33 +01:00
2021-07-19 18:11:21 +02:00
itest-race : build -itest -race itest -only
2021-06-14 16:24:02 +02:00
itest-parallel : build -itest db -instance
2020-11-04 11:03:33 +01:00
@$( call print, "Running tests" )
2022-08-11 13:39:40 +02:00
rm -rf itest/*.log itest/.logs-*; date
2021-01-06 19:42:08 +01:00
EXEC_SUFFIX = $( EXEC_SUFFIX) echo " $$ (seq 0 $$ (expr $( ITEST_PARALLELISM) - 1)) " | xargs -P $( ITEST_PARALLELISM) -n 1 -I { } scripts/itest_part.sh { } $( NUM_ITEST_TRANCHES) $( TEST_FLAGS) $( ITEST_FLAGS)
2020-07-31 10:32:30 +02:00
2021-11-10 17:47:14 +01:00
itest-clean :
@$( call print, "Cleaning old itest processes" )
killall lnd-itest || echo "no running lnd-itest process found" ;
2022-02-07 13:58:22 +01:00
unit : $( BTCD_BIN )
2018-03-29 07:16:18 +02:00
@$( call print, "Running unit tests." )
$( UNIT)
2018-03-28 13:22:52 +02:00
2022-02-07 13:58:22 +01:00
unit-debug : $( BTCD_BIN )
2021-02-17 17:47:14 +01:00
@$( call print, "Running debug unit tests." )
$( UNIT_DEBUG)
2019-01-18 12:17:58 +01:00
unit-cover : $( GOACC_BIN )
2018-03-29 07:16:18 +02:00
@$( call print, "Running unit coverage tests." )
2023-03-08 19:40:15 +01:00
$( GOACC)
2019-05-21 12:33:07 +02:00
2018-03-29 07:16:18 +02:00
unit-race :
@$( call print, "Running unit race tests." )
2018-12-03 00:24:14 +01:00
env CGO_ENABLED = 1 GORACE = "history_size=7 halt_on_errors=1" $( UNIT_RACE)
2018-03-29 07:16:18 +02:00
2023-01-26 20:03:03 +01:00
unit-bench : $( BTCD_BIN )
@$( call print, "Running benchmark tests." )
$( UNIT_BENCH)
2018-03-29 07:16:18 +02:00
# =============
# FLAKE HUNTING
# =============
2019-01-17 10:24:32 +01:00
flakehunter : build -itest
2019-05-24 14:17:48 +02:00
@$( call print, " Flake hunting ${ backend } integration tests. " )
2020-12-03 11:30:25 +01:00
while [ $$ ? -eq 0 ] ; do make itest-only icase = '${icase}' backend = '${backend}' ; done
2018-03-29 07:16:18 +02:00
flake-unit :
@$( call print, "Flake hunting unit tests." )
2019-03-07 02:30:28 +01:00
while [ $$ ? -eq 0 ] ; do GOTRACEBACK = all $( UNIT) -count= 1; done
2018-03-29 07:16:18 +02:00
2020-11-07 13:23:30 +01:00
flakehunter-parallel :
@$( call print, " Flake hunting ${ backend } integration tests in parallel. " )
while [ $$ ? -eq 0 ] ; do make itest-parallel tranches = 1 parallel = ${ ITEST_PARALLELISM } icase = '${icase}' backend = '${backend}' ; done
2020-09-23 14:55:08 +02:00
# =============
# FUZZING
# =============
2022-11-11 17:44:37 +01:00
fuzz :
2020-09-23 14:55:08 +02:00
@$( call print, " Fuzzing packages ' $( FUZZPKG) '. " )
2022-11-11 17:44:37 +01:00
scripts/fuzz.sh run " $( FUZZPKG) " " $( FUZZ_TEST_RUN_TIME) " " $( FUZZ_NUM_PROCESSES) "
2020-09-23 14:55:08 +02:00
2018-03-29 07:16:18 +02:00
# =========
# UTILITIES
# =========
2018-03-28 13:22:52 +02:00
2022-02-07 13:58:22 +01:00
fmt : $( GOIMPORTS_BIN )
2018-11-06 15:18:24 +01:00
@$( call print, "Fixing imports." )
2022-02-07 13:58:20 +01:00
gosimports -w $( GOFILES_NOVENDOR)
2018-03-29 07:16:18 +02:00
@$( call print, "Formatting source." )
2018-10-25 04:06:58 +02:00
gofmt -l -w -s $( GOFILES_NOVENDOR)
2018-03-28 13:22:52 +02:00
2022-08-22 21:22:51 +02:00
fmt-check : fmt
@$( call print, "Checking fmt results." )
if test -n " $$ (git status --porcelain) " ; then echo "code not formatted correctly, please run `make fmt` again!" ; git status; git diff; exit 1; fi
2022-02-10 16:01:59 +01:00
lint : docker -tools
2018-03-29 07:16:18 +02:00
@$( call print, "Linting source." )
2022-02-10 16:01:59 +01:00
$( DOCKER_TOOLS) golangci-lint run -v $( LINT_WORKERS)
2018-03-28 13:22:52 +02:00
2018-03-29 07:16:18 +02:00
list :
@$( call print, "Listing commands." )
@$( MAKE) -qp | \
awk -F':' '/^[a-zA-Z0-9][^$$#\/\t=]*:([^=]|$$)/ {split($$1,A,/ /);for(i in A)print A[i]}' | \
grep -v Makefile | \
sort
2018-01-29 17:11:03 +01:00
2018-03-29 07:16:18 +02:00
rpc :
@$( call print, "Compiling protos." )
2021-01-15 13:44:50 +01:00
cd ./lnrpc; ./gen_protos_docker.sh
2018-03-28 13:22:52 +02:00
2019-05-23 00:24:40 +02:00
rpc-format :
@$( call print, "Formatting protos." )
cd ./lnrpc; find . -name "*.proto" | xargs clang-format --style= file -i
2020-03-11 10:17:45 +01:00
rpc-check : rpc
2019-05-23 00:24:40 +02:00
@$( call print, "Verifying protos." )
2021-07-27 12:59:54 +02:00
cd ./lnrpc; ../scripts/check-rest-annotations.sh
2022-01-27 07:52:03 +01:00
if test -n " $$ (git status --porcelain) " ; then echo "Protos not properly formatted or not compiled with v3.4.0" ; git status; git diff; exit 1; fi
2019-05-23 00:24:40 +02:00
2021-08-03 13:37:58 +02:00
rpc-js-compile :
@$( call print, "Compiling JSON/WASM stubs." )
2021-11-16 02:13:38 +01:00
GOOS = js GOARCH = wasm $( GOBUILD) -tags= " $( WASM_RELEASE_TAGS) " $( PKG) /lnrpc/...
2021-08-03 13:37:58 +02:00
2020-10-08 09:10:59 +02:00
sample-conf-check :
@$( call print, "Making sure every flag has an example in the sample-lnd.conf file" )
for flag in $$ ( GO_FLAGS_COMPLETION = 1 go run -tags= " $( RELEASE_TAGS) " $( PKG) /cmd/lnd -- | grep -v help | cut -c3-) ; do if ! grep -q $$ flag sample-lnd.conf; then echo " Command line flag -- $$ flag not added to sample-lnd.conf " ; exit 1; fi ; done
2021-01-15 13:44:54 +01:00
mobile-rpc :
2022-05-11 19:18:17 +02:00
@$( call print, "Creating mobile RPC from protos." )
cd ./lnrpc; COMPILE_MOBILE = 1 SUBSERVER_PREFIX = 1 ./gen_protos_docker.sh
2019-01-24 14:51:05 +01:00
2019-01-24 14:51:06 +01:00
vendor :
@$( call print, "Re-creating vendor directory." )
2021-10-01 21:45:39 +02:00
rm -r vendor/; go mod vendor
2019-01-24 14:51:06 +01:00
2023-01-27 21:38:38 +01:00
apple : mobile -rpc
2022-03-26 22:20:30 +01:00
@$( call print, " Building iOS and macOS cxframework ( $( IOS_BUILD) ). " )
mkdir -p $( IOS_BUILD_DIR)
2023-01-25 18:23:43 +01:00
$( GOMOBILE_BIN) bind -target= ios,iossimulator,macos -tags= " mobile $( DEV_TAGS) $( RPC_TAGS) " -ldflags " $( RELEASE_LDFLAGS) " -v -o $( IOS_BUILD) $( MOBILE_PKG)
2022-03-26 22:20:30 +01:00
2023-01-27 21:38:38 +01:00
ios : mobile -rpc
2022-03-26 22:15:08 +01:00
@$( call print, " Building iOS cxframework ( $( IOS_BUILD) ). " )
2019-01-24 14:51:06 +01:00
mkdir -p $( IOS_BUILD_DIR)
2023-01-25 18:23:43 +01:00
$( GOMOBILE_BIN) bind -target= ios,iossimulator -tags= " mobile $( DEV_TAGS) $( RPC_TAGS) " -ldflags " $( RELEASE_LDFLAGS) " -v -o $( IOS_BUILD) $( MOBILE_PKG)
2019-01-24 14:51:06 +01:00
2023-01-27 21:38:38 +01:00
macos : mobile -rpc
2022-03-26 22:19:37 +01:00
@$( call print, " Building macOS cxframework ( $( IOS_BUILD) ). " )
mkdir -p $( IOS_BUILD_DIR)
2023-01-25 18:23:43 +01:00
$( GOMOBILE_BIN) bind -target= macos -tags= " mobile $( DEV_TAGS) $( RPC_TAGS) " -ldflags " $( RELEASE_LDFLAGS) " -v -o $( IOS_BUILD) $( MOBILE_PKG)
2022-03-26 22:19:37 +01:00
2023-01-27 21:38:38 +01:00
android : mobile -rpc
2019-01-24 14:51:06 +01:00
@$( call print, " Building Android library ( $( ANDROID_BUILD) ). " )
mkdir -p $( ANDROID_BUILD_DIR)
2023-01-27 21:38:38 +01:00
$( GOMOBILE_BIN) bind -target= android -androidapi 21 -tags= " mobile $( DEV_TAGS) $( RPC_TAGS) " -ldflags " $( RELEASE_LDFLAGS) " -v -o $( ANDROID_BUILD) $( MOBILE_PKG)
2019-01-24 14:51:06 +01:00
mobile : ios android
2018-03-28 13:22:52 +02:00
clean :
2018-03-29 07:16:18 +02:00
@$( call print, " Cleaning source. $( NC) " )
2018-09-13 23:17:33 +02:00
$( RM) ./lnd-debug ./lncli-debug
2018-11-02 09:49:32 +01:00
$( RM) ./lnd-itest ./lncli-itest
2018-09-13 23:17:33 +02:00
$( RM) -r ./vendor .vendor-new
2018-03-29 07:16:18 +02:00
2021-02-05 13:11:35 +01:00
clean-mobile :
@$( call print, "Cleaning autogenerated mobile RPC stubs." )
$( RM) -r mobile/build
$( RM) mobile/*_generated.go
2018-03-29 07:16:18 +02:00
.PHONY : all \
2018-09-09 10:48:37 +02:00
btcd \
2018-03-29 07:16:18 +02:00
default \
build \
install \
scratch \
check \
2018-10-11 10:31:04 +02:00
itest-only \
2018-03-29 07:16:18 +02:00
itest \
unit \
2021-02-17 17:47:14 +01:00
unit-debug \
2018-03-29 07:16:18 +02:00
unit-cover \
unit-race \
flakehunter \
flake-unit \
fmt \
lint \
list \
rpc \
2019-05-23 00:24:40 +02:00
rpc-format \
rpc-check \
2021-08-03 13:37:58 +02:00
rpc-js-compile \
2019-01-24 14:51:05 +01:00
mobile-rpc \
2019-01-24 14:51:06 +01:00
vendor \
ios \
2019-01-24 14:51:06 +01:00
android \
mobile \
2018-03-29 07:16:18 +02:00
clean