#!/usr/bin/make -f

include /usr/share/dpkg/pkg-info.mk

# Same value dh-go reads into DH_GOPKG, which it does not export.
GO_IMPORT_PATH := $(shell sed -n 's/^XS-Go-Import-Path: *//p' debian/control)

# Non-Go files needed at build time. dh-go copies only *.go, go.mod, go.sum and
# testdata/ contents into the build tree, so every go:embed target must be
# listed here. go:embed refuses symlinks, which is why dh-go copies files.
export DH_GOLANG_INSTALL_EXTRA := \
	internal/capabilities/appconfig.yaml \
	syft/pkg/cataloger/internal/cpegenerate/dictionary/data/cpe-index.json \
	$(wildcard syft/pkg/cataloger/*/capabilities.yaml) \
	$(wildcard cmd/syft/cli/ui/__snapshots__) \
	$(wildcard cmd/syft/internal/ui/__snapshots__)

# Code generators, example programs and the test suites that need docker or the
# network. Each entry is justified in debian/README.source.
#
# The patterns are Perl regular expressions matched twice: unanchored against
# the import paths from `go list` when choosing build targets, and unanchored
# against file paths relative to the source root when installing sources, since
# DH_GOLANG_EXCLUDES_ALL defaults to 1 from compat 12. `(/|$$)` is what works in
# both: it matches the import path that ends there and the files underneath it,
# and it stops a directory name from swallowing its longer siblings.
#
# The manager entry is anchored with `$$` alone on purpose. It is a `main`
# package that would install a stray /usr/bin/manager, but its testutil,
# internal and internal/config subpackages are imported by the shipped test of
# syft/pkg/cataloger/binary. Excluding the subtree left the installed sources
# unable to build their own tests, which only shows up under autopkgtest.
export DH_GOLANG_EXCLUDES := \
	examples/ \
	test/cli(/|$$) \
	test/install(/|$$) \
	internal/spdxlicense/generate(/|$$) \
	internal/capabilities/generate(/|$$) \
	internal/jsonschema(/|$$) \
	internal/packagemetadata/generate(/|$$) \
	internal/sourcemetadata/generate(/|$$) \
	syft/pkg/cataloger/binary/internal/manager$$ \
	cpegenerate/dictionary/index-generator(/|$$) \
	cmd/syft/internal/test/integration(/|$$)

%:
	dh $@ --with=golang --buildsystem=golang

override_dh_auto_build:
	# PIE is not the default for go install; enable it for ASLR hardening.
	dh_auto_build -- -buildmode=pie \
		-ldflags="-X main.version=$(DEB_VERSION_UPSTREAM)"

# Tests skipped, grouped by reason. debian/README.source argues each group.
# Measured by running the suite offline against upstream's own dependency
# versions, not guessed.

# Need ELF, Mach-O, PE or Java fixtures that upstream builds with its Makefile
# and .gitignores, so they never reach the source tarball.
SKIP_FIXTURES := Test_findELFSecurityFeatures|Test_elfHasEntrypoint|Test_elfHasExports|Test_machoHasEntrypoint|Test_machoHasExports|Test_machoUniversal|Test_peHasEntrypoint|Test_peHasExports|Test_ELFPackageCataloger|Test_PEPackageCataloger|Test_Cataloger_DefaultClassifiers_PositiveCases_Image|TestFetchPkgs|TestParseNativeImage|TestParseRpmFiles|Test_DBCataloger|Test_JavaBinaryImage|Test_PackageCataloger_SitePackageRelationships|TestParseGoBinary_PlainBinaryStillYieldsPackages|TestParseJar|TestParseNestedJar|TestReadContentsAndBuildInfo_FallbackToFoundBytesReportsNoGap|TestReadContentsAndBuildInfo_GapSurvivesBuildInfoFromTheReconstruction|TestScanFile_FakeUPXHeaderStillYieldsPackages|TestScanFile_UnpackedBinaryReadsTheFileItWasGiven|TestScanReader_PartialReconstructionStillYieldsItsPackages|TestScanReader_SizeRefusalOnAnUnpackedBinaryIsNotReported|Test_getCryptoInformation_passesThroughNonELF|Test_getGOARCHFromBin|Test_parseTarWrappedJavaArchive|Test_parseZipWrappedJavaArchive|TestParseBunLock|TestParseBunLock_ExcludeDevDependencies|TestParseBunLock_Fixtures|TestParseDenoLock|Test_corruptDenoLock

# Build a container image through stereoscope's imagetest, which shells out to
# docker and pulls from docker.io.
SKIP_DOCKER := TestAllLayersImageResolver_FilesContents|TestAllLayersImageResolver_FilesContents_errorOnDirRequest|TestAllLayersResolver_AllLocations|TestAllLayersResolver_FilesByGlob|TestAllLayersResolver_FilesByPath|TestCataloger|TestCataloger_Image|TestCataloger_Image_FilesListing|TestDigestsCataloger_MixFileTypes|TestDotnetDepsCataloger_regressions|TestDpkgArchiveCataloger|TestFileDigestCataloger_GivenCoordinates|TestFileMetadataCataloger|TestFileMetadataCataloger_GivenCoordinates|TestImageResolvers_Hardlinks|TestImageSquashResolver_FilesByGlob|TestImageSquashResolver_FilesByPath|TestNewAuditBinaryCataloger|TestSquashImageResolver_FilesContents|TestSquashImageResolver_FilesContents_errorOnDirRequest|TestSquashResolver_AllLocations|TestStoreCataloger_Image|Test_ContainerImageDeepSquash_AllLocations|Test_ContainerImageDeepSquash_FilesByGlob|Test_ContainerImageDeepSquash_FilesByPath|Test_ContainerImageDeepSquash_FilesContents|Test_ContainerImageDeepSquash_FilesContents_errorOnDirRequest|Test_ContainerImageDeepSquash_resolvesLinks|Test_EncodeDecodeCycle|Test_InterpreterCataloger|Test_JSONSchemaConventions|Test_KernelCataloger|Test_PackageCataloger_Binary|Test_PackageCataloger_Binary_SymbolsFromPackedBinary|Test_Read_DotNetDetection|Test_allRegularFiles|Test_extractDepsJSONFromBundle_Versions|Test_imageAllLayersResolver_resolvesLinks|Test_imageSquashResolver_resolvesLinks|Test_parseGoSource_replacedModulesAreNotDuplicated|TestCycloneDxDirectoryEncoder|TestCycloneDxImageEncoder|TestDpkgCataloger|TestGithubImageEncoder|Test_ContainerImageDeepSquash_FilesByMIMEType|Test_ContainerImageDeepSquash_hasFilesystemIDInLocation|Test_EnvironmentTask|Test_StereoscopeImage_Exclusions|Test_imageAllLayersResolver_FilesByMIMEType|Test_imageAllLayersResolver_hasFilesystemIDInLocation|Test_imageSquashResolver_FilesByMIMEType|Test_imageSquashResolver_hasFilesystemIDInLocation|TestCpanCatalogers_buildLeftoversAndVendoredRelease|TestCpanCatalogers_coreDistributionsNotCataloged|TestCpanCatalogers_metadataTypes|TestCpanCatalogers_vendoredReleaseCannotMerge|TestCpanInstalledCataloger_mirrorInstallEvidence|TestCpanInstalledCataloger_mirrorInstalls|TestCpanInstalledCataloger_packlistOnly|TestCpanInstalledCataloger_prereqRelationships|TestCpanInstalledCataloger_twoInstallTrees|TestCpanMetaCataloger_unpackedReleases|TestImageSmallUPXNeedsLoaderPadding|TestInstallJSON_clientFormatting

# Look for the repository root with git, which a source package is not, or expect
# a write to fail where the build runs as root, or need the module cache and the
# network: Test_parseGoSource_packageResolution resolves a whole module graph and
# reads the licence of each dependency, and returns nil without them.
SKIP_ENV := TestAllNames|TestDiscoverTypeNames_byExample|Test_errors|Test_parseGoSource_packageResolution

# Compare against the golden snapshots under testdata/snapshot. The artifact IDs
# in them are hashes computed with github.com/gohugoio/hashstructure, and upstream
# pinned 0.6.0 while unstable has 1.0.0, so every ID differs from the recorded
# one: expected SPDXRef-Package-python-package-1-f04d218ff5ff50db, got
# ...-b6cedb8819b34eed. Nothing is wrong with the package; the IDs are opaque and
# stable within a run.
SKIP_SNAPSHOT := TestDirectoryEncoder|TestImageEncoder|TestEncodeFullJSONDocument|Test_FileComponents|TestSPDX22JSONRequredProperties|TestSPDXJSONDirectoryEncoder|TestSPDXJSONImageEncoder|TestSPDXJSONSPDXIDs|TestSPDXRelationshipOrder|TestSPDXTagValueDirectoryEncoder|TestSPDXTagValueImageEncoder

# Their fixtures include symlinks, among them dangling ones, which do not survive
# into the build tree, so the file lists they compare come out short.

override_dh_auto_test:
	dh_auto_test -- -short \
		-skip '^($(SKIP_FIXTURES)|$(SKIP_DOCKER)|$(SKIP_ENV)|$(SKIP_SNAPSHOT))$$'

execute_after_dh_auto_configure:
	# One defect left in the go.work that dh-go generates, in
	# _create_go_work_file: https://bugs.debian.org/1149023. The other one,
	# https://bugs.debian.org/1149022, is fixed since dh-go 1.69, which is why
	# Build-Depends asks for that version.
	#
	# dh-go writes "replace M => ./src/M" for any directory that exists under
	#    _build/src, without checking that it holds a go.mod. Debian ships
	#    klauspost/cpuid only as the /v2 module, so the parent directory is there
	#    but empty, and Go stops with "reading src/.../go.mod: no such file or
	#    directory". The stub declares go 1.21, not the 1.16 that a go.mod without
	#    a go directive implies, or anything in it that uses generics would fail to
	#    compile. Point those at a stub of our own instead of writing into the
	#    replace target: some targets are symlinks into /usr, where the write fails
	#    and would leave the workspace broken without saying so. Nothing imports
	#    those modules; Go only reads them to resolve the module graph.
	#
	#    Under autopkgtest there is no go.work to rewrite yet. dh_golang_autopkgtest
	#    overrides dh_auto_configure, so dh-go writes the file later, from
	#    get_targets inside dh_auto_build, after this hook has run. The same
	#    modules are reachable from go.sum, and there the replace target is a real
	#    copy of the installed sources, so the stub goes into it.
	#
	# Each branch sits in one shell command because make gives every recipe line a
	# shell of its own, where a guard on its own line could not skip the rest.
	if [ -f _build/go.work ]; then \
		sed -n 's|^replace \([^ ]*\) => \./\(src/.*\)$$|\1 \2|p' _build/go.work \
		| while read -r mod dir; do \
			if [ ! -e "_build/$$dir/go.mod" ]; then \
				mkdir -p "_build/stubs/$$mod"; \
				printf 'module %s\n\ngo 1.21\n' "$$mod" > "_build/stubs/$$mod/go.mod"; \
				sed -i "s|^replace $$mod => \./$$dir$$|replace $$mod => ./stubs/$$mod|" _build/go.work; \
				echo "stub module for $$mod"; \
			fi; \
		done; \
	fi
	if [ ! -f _build/go.work ] && [ -f _build/src/$(GO_IMPORT_PATH)/go.sum ]; then \
		awk '{print $$1}' _build/src/$(GO_IMPORT_PATH)/go.sum | sort -u \
		| while read -r mod; do \
			d="_build/src/$$mod"; \
			if [ -d "$$d" ] && [ ! -h "$$d" ] && [ ! -e "$$d/go.mod" ]; then \
				printf 'module %s\n\ngo 1.21\n' "$$mod" > "$$d/go.mod"; \
				echo "stub go.mod in $$d"; \
			fi; \
		done; \
	fi

execute_after_dh_auto_install:
	# .make is upstream's build tooling as a nested Go module, and its module
	# path ends in /.make. A path component starting with a dot is not valid in
	# a go.work replace directive, so shipping it can make dh-go write a go.work
	# that Go refuses to parse for anything build-depending on this package, and
	# it requires anchore/go-make, which is not in Debian. None of it is library
	# code or test data.
	# The glob matters: this package has .install files, so dh_auto_install
	# stages into debian/tmp and dh_install copies from there after this hook.
	rm -rf debian/*/usr/share/gocode/src/github.com/anchore/syft/.make
	# The go-snaps snapshots come with the executable bit set upstream.
	find debian -name '*.snap' -type f -exec chmod -x {} +
	# The test suite writes fixtures as it runs, and dh_auto_install copies them
	# along with the rest of testdata. None of these 50 files is in the orig
	# tarball, so none of them belongs in a binary package: 35 observation files
	# written by the cataloger test helper, the jar and hpi fixtures the Java tests
	# build with zip, one zip and one mtree.
	find debian -name test-observations.json -delete
	rm -rf debian/*/usr/share/gocode/src/github.com/anchore/syft/syft/pkg/cataloger/java/testdata/jar-metadata/cache
	rm -f debian/*/usr/share/gocode/src/github.com/anchore/syft/internal/file/testdata/zip-source/nested.zip
	rm -f debian/*/usr/share/gocode/src/github.com/anchore/syft/syft/pkg/cataloger/arch/testdata/installed/var/lib/pacman/local/bombpkg-1.0-1/mtree

execute_after_dh_golang:
	# Built-Using is the wrong field for a statically linked Go binary since
	# Policy clarified it in 2017, and Static-Built-Using is the right one, which
	# dh_golang also sets. dh_golang still writes misc:Built-Using for
	# back-compat, and from compat 14 dh_gencontrol injects every relationship
	# substvar whether debian/control asks for it or not, so dropping the field
	# from control is not enough: the 11 KB list comes back and lintian reports
	# field-too-long. Remove the substvar itself, from whichever packages this
	# build produces: find, not a glob, because a binary-indep or binary-arch
	# build writes substvars for one package or for neither, and an unmatched
	# glob reaches sed as a literal path and stops the build.
	find debian -maxdepth 1 -name '*.substvars' \
		-exec sed -i '/^misc:Built-Using/d' {} +
