#!/usr/bin/make -f
# -*- makefile -*-
#
# These rules should work for any debian-ish distro that uses systemd
# as init.  That does _not_ include Ubuntu 14.04 ("trusty"); look for
# its own special rule file.
#
# Please keep the diff between that and this relatively small, even if
# it means having suboptimal code; these need to be kept in sync by
# sentient bags of meat.

#export DH_VERBOSE=1
export DH_OPTIONS
export DH_GOPKG := github.com/snapcore/snapd
#export DEB_BUILD_OPTIONS=nocheck
export DH_GOLANG_EXCLUDES=tests
export DH_GOLANG_GO_GENERATE=1

VERSION := $(shell dpkg-parsechangelog -S Version)

ifneq (,$(findstring +fips,$(VERSION)))
	# the version has +fips tag, we're building a FIPS variant
	FIPSBUILD = 1
else
	FIPSBUILD = 0
endif

export PATH:=${PATH}:${CURDIR}
# make sure that correct go version is found on xenial
export PATH:=/usr/lib/go-1.18/bin:${PATH}
ifeq (${FIPSBUILD},1)
	# when building with FIPS, use Go 1.23 which is also declared in build
	# dependencies; during testing it is manually installed, but during LP builds
	# it should be pulled in as a build dependency
	export PATH:=/usr/lib/go-1.23/bin:${PATH}
endif
# GOCACHE is needed by go-1.13+
export GOCACHE:=/tmp/go-build

include /etc/os-release

SUBSTVARS =
# On 18.04 the released version of apt (1.6.1) has a bug that causes
# problem on "apt purge snapd". To ensure this won't happen add the
# right dependency on 18.04.
ifeq (${VERSION_ID},"18.04")
	SUBSTVARS += -Vsnapd:Breaks="systemd (<< 237-3ubuntu10.24), apt (<< 1.6.3)"
endif
# Same as above for 18.10 just a different version.
ifeq (${VERSION_ID},"18.10")
	SUBSTVARS += -Vsnapd:Breaks="apt (<< 1.7.0~alpha2)"
endif
# Since 21.10 is using cgroups v2, having a session bus is a hard requirement,
# for earlier versions it's nice to have and allows snaps to be tracked
# correctly. However, the (default-)dbus-session-bus virtual packages were only
# introduced in 2018, so earlier supported releases (16.04) have to explicitly
# specify the requirement.
ifneq (${VERSION_ID},"16.04")
	# version with appropriate virtual packages
	SUBSTVARS += -Vdbussession:Depends="default-dbus-session-bus | dbus-session-bus"
endif

# Restart snapd only after the upgrade, this does not work on 16.04 so
# we keep the existing behavior there, see
# https://bugs.launchpad.net/ubuntu/+source/snapd/+bug/1969162
DH_SYSTEMD_START_OPTS=
ifneq (${VERSION_ID},"16.04")
	DH_SYSTEMD_START_OPTS += --restart-after-upgrade
endif

# this is overridden in the ubuntu/14.04 release branch
SYSTEMD_UNITS_DESTDIR="lib/systemd/system/"

# The go tool does not fully support vendoring with gccgo, but we can
# work around that by constructing the appropriate -I flag by hand.
GCCGO := $(shell go tool dist env > /dev/null 2>&1 && echo no || echo yes)

BUILDFLAGS:=-pkgdir=$(CURDIR)/_build/std
BUILDFLAGS+=-ldflags=-w
# Disable -buildmode=pie mode on all our 32bit platforms
# (i386 and armhf). For i386 because of LP: #1711052 and for
# armhf because of LP: #1822738
ifeq ($(shell dpkg-architecture -qDEB_HOST_ARCH_BITS),64)
 BUILDFLAGS+= -buildmode=pie
endif

GCCGOFLAGS=
ifeq ($(GCCGO),yes)
GOARCH := $(shell go env GOARCH)
GOOS := $(shell go env GOOS)
BUILDFLAGS:=
GCCGOFLAGS=-gccgoflags="-I $(CURDIR)/_build/pkg/gccgo_$(GOOS)_$(GOARCH)/$(DH_GOPKG)/vendor"
export DH_GOLANG_GO_GENERATE=0
# workaround for https://github.com/golang/go/issues/23721
export GOMAXPROCS=2
endif

# build with "tpm" support on ubuntu by default
# TAGS are the go build tags for all binaries, SNAP_TAGS are for snap and
# snap-bootstrap build only.
_TAGS=
_SNAP_TAGS=
# check if we need to include the testkeys in the binary
ifneq (,$(filter testkeys,$(DEB_BUILD_OPTIONS)))
  # if enabled also enable bootloader assets testing and fault injection
	_TAGS := withtestkeys,withbootassetstesting,faultinject,statelocktrace
	_SNAP_TAGS := nomanagers,withtestkeys,faultinject,statelocktrace
else
	_SNAP_TAGS=nomanagers
endif

ifeq (${FIPSBUILD},1)
  # if enabled also enable bootloader assets testing and fault injection
	_TAGS := $(_TAGS),goexperiment.opensslcrypto,snapdfips
	_SNAP_TAGS := $(_SNAP_TAGS),goexperiment.opensslcrypto,snapdfips
endif

TAGS=-tags "$(_TAGS)"
SNAP_TAGS=-tags "$(_SNAP_TAGS)"

DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)

BUILT_USING_PACKAGES=
# export DEB_BUILD_MAINT_OPTIONS = hardening=+all
# DPKG_EXPORT_BUILDFLAGS = 1
# include /usr/share/dpkg/buildflags.mk

# Currently, we enable confinement for Ubuntu only, not for derivatives,
# because derivatives may have different kernels that don't support all the
# required confinement features and we don't to mislead anyone about the
# security of the system.  Discuss a proper approach to this for downstreams
# if and when they approach us.
ifeq ($(shell dpkg-vendor --query Vendor),Ubuntu)
    # On Ubuntu 16.04 we need to produce a build that can be used on wide
    # variety of systems. As such we prefer static linking over dynamic linking
    # for stability, predicability and easy of deployment. We need to link some
    # things dynamically though: udev has no stable IPC protocol between
    # libudev and udevd so we need to link with it dynamically.
    VENDOR_ARGS=--enable-nvidia-multiarch --enable-static-libcap --enable-static-libapparmor --with-host-arch-triplet=$(DEB_HOST_MULTIARCH)
ifeq ($(shell dpkg-architecture -qDEB_HOST_ARCH),amd64)
		VENDOR_ARGS+= --with-host-arch-32bit-triplet=$(shell dpkg-architecture -f -ai386 -qDEB_HOST_MULTIARCH)
endif
    BUILT_USING_PACKAGES=libcap-dev libapparmor-dev libseccomp-dev
else
ifeq ($(shell dpkg-vendor --query Vendor),Debian)
    BUILT_USING_PACKAGES=libcap-dev
else
    VENDOR_ARGS=--disable-apparmor
endif
endif
BUILT_USING=$(shell dpkg-query -f '$${source:Package} (= $${source:Version}), ' -W $(BUILT_USING_PACKAGES))

%:
ifneq ($(shell dpkg-architecture -qDEB_HOST_ARCH),powerpc)
	dh $@ --buildsystem=golang --with=golang --fail-missing --with systemd --builddirectory=_build
else
	# "powerpc" is not supported unfortunately, do nothing here
	# See https://github.com/tianon/debian-runc/compare/70957b315f82170dc2ab7085d39c23835c0fa996...xenial for a more elaborate version of this idea.
	# Note that snapd never really worked on powerpc (we never had a core
	# snap there) so we don't need to show anything to the user
	if [ "$@" = "clean" ]; then \
		rm -rf debian/snapd; \
	fi; \
	if [ "$@" = "binary" ] || [ "$@" = "binary-arch" ]; then \
		install -m755 -d debian/snapd/usr/share/doc/snapd/; \
		cp debian/README.powerpc debian/snapd/usr/share/doc/snapd/; \
		dh_installdeb; \
		dh_gencontrol; \
		dh_builddeb; \
	fi;
endif

override_dh_fixperms:
	dh_fixperms -Xusr/lib/snapd/snap-confine


# The .real profile is a workaround for a bug in dpkg LP: #1673247 that causes
# ubiquity to crash. It allows us to "move" the snap-confine profile from
# snap-confine into snapd in a way that works with old dpkg that is in the live
# CD image.
#
# Because both the usual and the .real profile describe the same binary the
# .real profile takes priority (as it is loaded later).
#
# We need run dh_apparmor *before* dh_systemd_enable to ensure the postinst
# snippets are added in the right order (first the new apparmor profile
# is loaded, then we restart the service).
override_dh_systemd_enable:
	dh_apparmor --profile-name=usr.lib.snapd.snap-confine.real -psnapd
	dh_systemd_enable

override_dh_clean:
	dh_clean
	$(MAKE) -C data clean
	# XXX: hacky
	$(MAKE) -C cmd distclean || true
	# XXX: hacky^2
	(cd c-vendor/squashfuse && rm -f snapfuse && make distclean || true )

override_dh_auto_build:
	# very ugly test for FIPS variant of a toolchain
	# see https://warthogs.atlassian.net/browse/FR-8860
ifeq (${FIPSBUILD},1)
	if ! test -f /usr/lib/go-1.23/src/crypto/internal/backend/openssl_linux.go; then \
		echo "Go 1.23 FIPS toolchain not found"; \
		exit 1; \
	fi
endif

	# generate dh-golang does not copy modules.txt
	cp -a vendor/modules.txt _build/src/$(DH_GOPKG)/vendor
	# usually done via `go generate` but that is not supported on powerpc
	./mkversion.sh
	# ensure auto-generated version is also in the build-tree
	(cd _build/src/$(DH_GOPKG)/ && ../../../../../mkversion.sh)
	# Build golang bits
	mkdir -p _build/src/$(DH_GOPKG)/cmd/snap/test-data
	cp -a cmd/snap/test-data/*.gpg _build/src/$(DH_GOPKG)/cmd/snap/test-data/
	cp -a bootloader/assets/data _build/src/$(DH_GOPKG)/bootloader/assets

	# this is the main go build

	# note that dh-golang invokes go generate as the first step, which may invoke
	# go
	GOINVOKEFLAGS='-mod=vendor'	GO111MODULE=on SNAPD_VANILLA_GO=$$(which go) \
	PATH="$$(pwd)/packaging/build-tools/:$$PATH" \
		dh_auto_build -- -mod=vendor $(BUILDFLAGS) $(TAGS) $(GCCGOFLAGS) $(DH_GOPKG)/cmd/...

	(cd _build/bin && GOPATH=$$(pwd)/.. go build -mod=vendor $(BUILDFLAGS) $(GCCGOFLAGS) $(SNAP_TAGS) $(DH_GOPKG)/cmd/snap)
	(cd _build/bin && GOPATH=$$(pwd)/.. go build -mod=vendor $(BUILDFLAGS) $(GCCGOFLAGS) $(SNAP_TAGS) $(DH_GOPKG)/cmd/snap-bootstrap)

	# Generate static snap-exec, snapctl and snap-update-ns - it somehow includes CGO so
	# we must force a static build here. We need a static snap-{exec,update-ns}/snapctl
	# inside the core snap because not all bases will have a libc
	(cd _build/bin && GOPATH=$$(pwd)/.. CGO_ENABLED=0 go build -mod=vendor $(GCCGOFLAGS) $(DH_GOPKG)/cmd/snap-exec)
	(cd _build/bin && GOPATH=$$(pwd)/.. CGO_ENABLED=0 go build -mod=vendor $(GCCGOFLAGS) $(DH_GOPKG)/cmd/snapctl)
	(cd _build/bin && GOPATH=$$(pwd)/.. go build -mod=vendor --ldflags '-extldflags "-static"' $(GCCGOFLAGS) $(DH_GOPKG)/cmd/snap-update-ns)

	# ensure we generated a static build
	$(shell	if ldd _build/bin/snap-exec; then false "need static build"; fi)
	$(shell	if ldd _build/bin/snap-update-ns; then false "need static build"; fi)
	$(shell	if ldd _build/bin/snapctl; then false "need static build"; fi)

	# ensure snap-seccomp is build with a static libseccomp on Ubuntu
ifeq ($(shell dpkg-vendor --query Vendor),Ubuntu)
	sed -i "s|#cgo LDFLAGS:|#cgo LDFLAGS: /usr/lib/$(shell dpkg-architecture -qDEB_TARGET_MULTIARCH)/libseccomp.a|" _build/src/$(DH_GOPKG)/cmd/snap-seccomp/main.go
	(cd _build/src/$(DH_GOPKG) && GOCACHE=/tmp/go-build CGO_LDFLAGS_ALLOW="/.*/libseccomp.a" go build -o ../../../../bin/snap-seccomp -mod=vendor $(GCCGOFLAGS) ./cmd/snap-seccomp)
	# ensure that libseccomp is not dynamically linked
	ldd _build/bin/snap-seccomp
	test "$$(ldd _build/bin/snap-seccomp | grep libseccomp)" = ""
	# revert again so that the subsequent tests work
	sed -i "s|#cgo LDFLAGS: -l/usr/lib/$(shell dpkg-architecture -qDEB_TARGET_MULTIARCH)/libseccomp.a|#cgo LDFLAGS:|" _build/src/$(DH_GOPKG)/cmd/snap-seccomp/main.go
endif

	# Build C bits, sadly manually
	cd cmd && ( autoreconf -i -f )
	cd cmd && ( ./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --libexecdir=/usr/lib/snapd $(VENDOR_ARGS))
	$(MAKE) -C cmd all

	# Generate the real systemd/dbus/env config files
	$(MAKE) -C data all

	# build squashfuse and rename to snapfuse
	(cd c-vendor/squashfuse && mkdir -p autom4te.cache && ./autogen.sh --disable-demo && ./configure --disable-demo && make && mv squashfuse_ll snapfuse)

override_dh_auto_test:
	# skip running tests on riscv64 for now because they are too slow and fail
	# constantly
ifneq ($(shell dpkg-architecture -qDEB_HOST_ARCH),riscv64)
	GO111MODULE=on \
		dh_auto_test -- -mod=vendor $(BUILDFLAGS) $(TAGS) $(GCCGOFLAGS) $(DH_GOPKG)/...
endif

# a tested default (production) build should have no test keys
ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
	# check that only the main trusted account-keys are included
	for b in _build/bin/snapd _build/bin/snap-bootstrap _build/bin/snap-preseed; do \
	  [ $$(strings $$b |grep -c -E "public-key-sha3-384: [a-zA-Z0-9_-]{64}") -eq 2 ] && \
	  strings $$b |grep -c "^public-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk$$" && \
	  strings $$b |grep -c "^public-key-sha3-384: d-JcZF9nD9eBw7bwMnH61x-bklnQOhQud1Is6o_cn2wTj8EYDi9musrIT9z2MdAa$$"; \
	done;
	# same for snap-repair
	[ $$(strings _build/bin/snap-repair|grep -c -E "public-key-sha3-384: [a-zA-Z0-9_-]{64}") -eq 3 ]
	# common with snapd
	strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk$$"
	strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: d-JcZF9nD9eBw7bwMnH61x-bklnQOhQud1Is6o_cn2wTj8EYDi9musrIT9z2MdAa$$"
	# repair-root
	strings _build/bin/snap-repair|grep -c "^public-key-sha3-384: nttW6NfBXI_E-00u38W-KH6eiksfQNXuI7IiumoV49_zkbhM0sYTzSnFlwZC-W4t$$"
endif
ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
	# run the snap-confine tests
	$(MAKE) -C cmd -k check
	# and data files tests
	$(MAKE) -C data -k check
endif


debian/snapd.install: SYSTEMD_LIBDIR=$(shell pkg-config --variable=systemdutildir systemd)
debian/snapd.install: debian/snapd.install.in
	sed 's,@systemd-lib@,$(SYSTEMD_LIBDIR),g' $< >$@.tmp
	mv $@.tmp $@

override_dh_install: debian/snapd.install
	# we do not need this in the package, its just needed during build
	rm -rf ${CURDIR}/debian/tmp/usr/bin/xgettext-go
	# toolbelt is not shippable
	rm -f ${CURDIR}/debian/tmp/usr/bin/toolbelt
	# we do not like /usr/bin/snappy anymore
	rm -f ${CURDIR}/debian/tmp/usr/bin/snappy
	# i18n stuff
	mkdir -p debian/snapd/usr/share
	if [ -d share/locale ]; then \
		cp -R share/locale debian/snapd/usr/share; \
	fi
	# chrorder generator
	rm -f ${CURDIR}/debian/tmp/usr/bin/chrorder
	# bootloader assets generator
	rm -f ${CURDIR}/debian/tmp/usr/bin/genasset
	# asserts/info
	rm -f ${CURDIR}/debian/tmp/usr/bin/info
	# docs generator
	rm -f ${CURDIR}/debian/tmp/usr/bin/docs

	# Install snapd's systemd units / upstart jobs, done
	# here instead of debian/snapd.install because the
	# ubuntu/14.04 release branch adds/changes bits here
	$(MAKE) -C data install DESTDIR=$(CURDIR)/debian/snapd/ \
		SYSTEMDSYSTEMUNITDIR=$(SYSTEMD_UNITS_DESTDIR)
	# We called this apps-bin-path.sh instead of snapd.sh, and
	# it's a conf file so we're stuck with it
	mv debian/snapd/etc/profile.d/snapd.sh debian/snapd/etc/profile.d/apps-bin-path.sh

	$(MAKE) -C cmd install DESTDIR=$(CURDIR)/debian/tmp
	# Permission 111 breaks deb builds on plucky e.g. "Can't opendir(debian/tmp/var/lib/snapd/void): Permission denied".
	# The desired permission 111 is re-applied in postinst.
	chmod 755 $(CURDIR)/debian/tmp/var/lib/snapd/void

	# Rename the apparmor profile, see dh_apparmor call above for an explanation.
	mv $(CURDIR)/debian/tmp/etc/apparmor.d/usr.lib.snapd.snap-confine $(CURDIR)/debian/tmp/etc/apparmor.d/usr.lib.snapd.snap-confine.real

	dh_install

override_dh_auto_install: snap.8
	dh_auto_install -O--buildsystem=golang

override_dh_golang:
	# XXX: dh_golang on older versions of ubuntu is not capable to
	# deal with newer go and go.mod. So just make this a no-op or
	# things will fail on e.g. 18.04. dh_golang will only add
	# "misc:Built-Using" information.
	true

snap.8:
	# fix reproducible builds as reported by:
	#   https://tests.reproducible-builds.org/debian/rb-pkg/unstable/amd64/snapd.html
	# once golang-go-flags is fixed we can remove the "sed" expression
	$(CURDIR)/_build/bin/snap help --man | sed '1 s/^.*/.TH snap 8 "$(shell date --reference=debian/changelog +"%d %B %Y")"/' > $@

override_dh_auto_clean:
	# XXX: workaround old dh-golang inability to remove _build/
	if [ -d _build ]; then cd _build && GOPATH=$$(pwd) go clean -modcache; fi
	dh_auto_clean -O--buildsystem=golang
	rm -vf snap.8

override_dh_gencontrol:
	dh_gencontrol -- -VBuilt-Using="$(BUILT_USING)" $(SUBSTVARS)

override_dh_systemd_start:
	# Due to https://github.com/systemd/systemd/issues/8102
	# debhelper supporting --remaining like behaviour here would
	# have been useful
	# (Re-)start socket first
	dh_systemd_start $(DH_SYSTEMD_START_OPTS) snapd.socket
	# Then service
	dh_systemd_start $(DH_SYSTEMD_START_OPTS) snapd.service
	# Then the rest
	dh_systemd_start $(DH_SYSTEMD_START_OPTS) $(filter-out snapd.socket snapd.service snapd.mounts-pre.target, $(shell ls debian/snapd/lib/systemd/system/))
