#!/usr/bin/make -f
# -*- makefile -*-
# Build gcc-mingw-w64 using gcc-10-source.
# Copyright © 2010-2020 Stephen Kitt <skitt@debian.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1

# Disable package mangling in Launchpad; it currently fails to parse
# Built-Using, which results in build failures
export NO_PKG_MANGLE=1

target_version := 10
target32 := i686-w64-mingw32
target64 := x86_64-w64-mingw32
targets := $(target32) $(target64)
threads := posix win32
gnat_arches := alpha amd64 arm64 armel armhf hppa i386 mips64el mipsel ppc64 ppc64el riscv64 s390x sh4 sparc64 x32

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

top_dir := $(shell pwd)
gcc_dir := /usr/src/gcc-$(target_version)
upstream_dir := $(top_dir)/src
build_dir := $(top_dir)/build
stampdir := $(top_dir)/stamps
source_version := $(shell dpkg-query -W -f="\$${Version}\n" gcc-$(target_version)-source)
deb_version := $(source_version)+$(DEB_VERSION)
deb_upstream_version := $(shell echo $(deb_version) | cut -d- -f1)
deb_upstream_nextversion := $(shell echo $(deb_upstream_version) | awk -F. "{ for (i = 1; i < NF; i++) { printf \"%d.\",\$$i; }; print (\$$NF + 1); }")
base_version := $(shell echo $(deb_version) | sed -e 's/\([1-9][.0-9]\).*-.*/\1/')
derives_from_ubuntu := $(shell (dpkg-vendor --derives-from Ubuntu && echo "yes") || echo "no")

# Split versions
first_split_version := 4.6.3-3+4
thread_split_version := 9.3.0-8+22
runtime_split_version := 9.3.0-8+22
ifeq ($(derives_from_ubuntu),yes)
  first_split_version := 4.6.3-1ubuntu5+5ubuntu1
# TODO Remember the split versions after Ubuntu syncs
endif

# libcaf migration version
libcaf_migration_version := 5.3.1-8+17
ifeq ($(derives_from_ubuntu),yes)
  libcaf_migration_version := 5.3.1-8ubuntu2+17
endif

ifneq ($(filter stage1,$(DEB_BUILD_PROFILES)),)
# Build the minimal gcc required to build mingw-w64
    languages := c,c++
    BUILD_TARGET := all-gcc
    INSTALL_TARGET := install-gcc
else
# Build the full GCC.
    languages := c,c++,fortran,objc,obj-c++
ifneq ($(filter $(DEB_HOST_ARCH),$(gnat_arches)),)
    languages := $(languages),ada
endif
    BUILD_TARGET :=
    INSTALL_TARGET := install install-lto-plugin
endif

comma := ,
space :=
space +=
control: debian/control
debian/control: $(wildcard debian/control.*) debian/rules
	echo \# This file is generated using debian/rules control, do not edit > debian/control
	cat debian/control.source >> debian/control
ifneq ($(filter stage1,$(DEB_BUILD_PROFILES)),)
	echo >> debian/control
	cat debian/control.bootstrap >> debian/control
else
	for language in $(subst $(comma),$(space),$(languages)); do \
		echo >> debian/control; \
		sed -f debian/control.$$language.sed debian/control.template >> debian/control; \
	done
	for target in $(subst -w64-mingw32,,$(subst _,-,$(targets))); do \
		for thread in $(threads); do \
			echo $$starget; \
			echo >> debian/control; \
			sed "s/@@TARGET@@/$$target/g;s/@@THREADS@@/$$thread/g" debian/control.runtime >> debian/control; \
		done; \
	done
endif
	echo >> debian/control
	cat debian/control.base >> debian/control
	sed -i '/^Recommends: $$/d' debian/control
	sed -i '/^Breaks: $$/d' debian/control
	sed -i '/^Conflicts: $$/d' debian/control
	sed -i '/^Replaces: $$/d' debian/control
	sed -i 's/@@VERSION@@/$(target_version)/g' debian/control
ifeq (,$(gnat_arches))
	sed -i '/@@GNAT_ARCHES@@/d' debian/control
else
	sed -i 's/@@GNAT_ARCHES@@/$(gnat_arches)/' debian/control
endif

# Hardening on the host, none on the target
# Format fails the build currently; using PIE produces a compiler
# which can't build with pre-compiled headers
dpkg_buildflags_host = DEB_BUILD_MAINT_OPTIONS="hardening=+all,-format,-pie" dpkg-buildflags
dpkg_buildflags_target = DEB_BUILD_MAINT_OPTIONS="hardening=-all" dpkg-buildflags

ifeq ($(filter stage1,$(DEB_BUILD_PROFILES)),)
CC = gcc-$(target_version)
CXX = g++-$(target_version)
else
CC = gcc
CXX = g++
endif
CFLAGS = $(shell $(dpkg_buildflags_host) --get CFLAGS)
CFLAGS += -Wall
CFLAGS_FOR_TARGET = $(shell $(dpkg_buildflags_target) --get CFLAGS)
CFLAGS_FOR_TARGET += -Wall
CPPFLAGS = $(shell $(dpkg_buildflags_host) --get CPPFLAGS)
CPPFLAGS_FOR_TARGET = $(shell $(dpkg_buildflags_target) --get CPPFLAGS)
CXXFLAGS = $(shell $(dpkg_buildflags_host) --get CXXFLAGS)
CXXFLAGS += -Wall
CXXFLAGS_FOR_TARGET = $(shell $(dpkg_buildflags_target) --get CXXFLAGS)
CXXFLAGS_FOR_TARGET += -Wall
FFLAGS = $(shell $(dpkg_buildflags_host) --get FFLAGS)
FFLAGS_FOR_TARGET = $(shell $(dpkg_buildflags_target) --get FFLAGS)
LDFLAGS = $(shell $(dpkg_buildflags_host) --get LDFLAGS)
LDFLAGS += -Wl,--as-needed
LDFLAGS_FOR_TARGET = $(shell $(dpkg_buildflags_target) --get LDFLAGS)
LDFLAGS_FOR_TARGET += -Wl,--as-needed
LDFLAGS_FOR_TARGET = -Xlinker --insert-timestamp=$(SOURCE_DATE_EPOCH)
export CC CXX CFLAGS CFLAGS_FOR_TARGET CPPFLAGS CPPFLAGS_FOR_TARGET
export CXXFLAGS CXXFLAGS_FOR_TARGET FFLAGS FFLAGS_FOR_TARGET LDFLAGS LDFLAGS_FOR_TARGET

# Number of jobs to run for build
ifeq ($(USE_NJOBS),no)
  NJOBS :=
  USE_CPUS := 1
else
  # Increase to 192 if building Java
  MEM_PER_CPU = 128
  NUM_CPUS := $(shell if echo $(USE_NJOBS) | grep -q -E '^[0-9]+$$'; \
                        then echo $(USE_NJOBS); \
                        else getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1; fi)
  USE_CPUS := $(shell mt=`awk '/^MemTotal/ { print $$2 }' /proc/meminfo`; \
                        awk -vn=$(NUM_CPUS) -vmt=$$mt -vm=$(MEM_PER_CPU) \
                                'END { mt/=1024; n2 = int(mt/m); print n==1 ? 1 : n2<n+1 ? n2 : n+1}' < /dev/null)
  ifneq (,$(strip $(USE_CPUS)))
    NJOBS := -j $(USE_CPUS)
  endif
endif

# Support parallel=<n> in DEB_BUILD_OPTIONS (see #209008)
ifneq (,$(filter parallel=%,$(subst $(COMMA), ,$(DEB_BUILD_OPTIONS))))
  NJOBS := -j $(subst parallel=,,$(filter parallel=%,$(subst $(COMMA), ,$(DEB_BUILD_OPTIONS))))
endif

# Patch targets
patchdir = $(gcc_dir)/debian/patches
series_file = series
unpack_stamp = $(stampdir)/unpack
patch_stamp = $(stampdir)/patch
GFDL_INVARIANT_FREE=yes
srcdir=$(upstream_dir)
include $(gcc_dir)/debian/rules.patch

unpack: $(unpack_stamp)
$(unpack_stamp):
	tar xf $(gcc_dir)/gcc-*.tar.*
	rm -rf $(upstream_dir)
	mv gcc-* $(upstream_dir)
# gcc-9-source assumes gm2 is unpacked too
	tar xf $(gcc_dir)/gm2-*.tar.* --strip-components 1 -C $(upstream_dir)
# Apply our first series of patches (those which need to be applied
# before gcc-?-source’s)
	QUILT_SERIES=debian/patches/series1 QUILT_PATCHES=debian/patches quilt push -a
# Drop the quilt cache so we can process other series
	rm -rf .pc
	mkdir -p $(stampdir)
# gcov-dump.texi is missing in some source tarballs
	if [ ! -f $(upstream_dir)/gcc/doc/gcov-dump.texi ]; then cp $(gcc_dir)/debian/dummy.texi $(upstream_dir)/gcc/doc/gcov-dump.texi; fi
	touch $@

clean: debian/control
	dh_testdir
	dh_testroot
	rm -rf $(stampdir) $(build_dir) $(upstream_dir) .pc autotools_files series *-stamp
	rm -f $(patsubst %.in,%,$(wildcard debian/*.in))
	dh_clean

# Configuration constructed as in the gcc package
PF=usr
libdir=lib
libexecdir=$(PF)/$(libdir)

# Standard Debian configuration flags
CONFFLAGS = \
	--prefix=/$(PF) \
	--enable-shared \
	--enable-static \
	--disable-multilib \
	--with-system-zlib \
	--libexecdir=/$(libexecdir) \
	--without-included-gettext \
	--libdir=/$(PF)/$(libdir) \
	--enable-libstdcxx-time=yes \
	--with-tune=generic
# Tell GCC where the headers are (this enables gcov)
CONFFLAGS += \
	--with-headers=/usr/$$target/include
# MinGW-w64 flags
# version-specific-runtime-libs puts target-specific libraries in
# /usr/lib/gcc rather than /usr/$(target)
CONFFLAGS += \
	--enable-version-specific-runtime-libs \
	--enable-fully-dynamic-string \
	--enable-libgomp
# Languages
CONFFLAGS += \
	--enable-languages=$(languages)
# LTO
CONFFLAGS += \
	--enable-lto
ifeq ($(filter stage1,$(DEB_BUILD_PROFILES)),)
# Thread-model-dependent
CONFFLAGS += \
	--enable-threads=$$threads \
	--program-suffix=-$$threads
endif
# Target-dependent
CONFFLAGS += \
	--program-prefix=$$target- \
	--target=$$target \
	--with-as=/usr/bin/$$target-as \
	--with-ld=/usr/bin/$$target-ld
# Enable libatomic
CONFFLAGS += \
	--enable-libatomic
# Enable experimental::filesystem and std::filesystem
CONFFLAGS += \
	--enable-libstdcxx-filesystem-ts=yes
# Enable dependency tracking (disabled by dh; see
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55930 for details)
CONFFLAGS += \
	--enable-dependency-tracking

spelling = grep -rl "$(1)" $(upstream_dir) | xargs -r sed -i "s/$(1)/$(2)/g"

# Patches applied or unapplied after the upstream sources have been
# unpacked and patched using the gcc-?-source package's patches
mingw-w64-patch: mingw-w64-patch-stamp
mingw-w64-patch-stamp: $(patch_stamp)
# Remove the patch building libstdc++_pic.a before configuring
	patch -R -p2 -d$(upstream_dir) < $(gcc_dir)/debian/patches/libstdc++-pic.diff
# Apply our second series of patches
	rm -rf .pc
	QUILT_SERIES=debian/patches/series2 QUILT_PATCHES=debian/patches quilt push -a
# Spelling fixes
	$(call spelling,Allow to,Allow one to)
	sed -i "s/Var befor:/Var before:/g" src/gcc/tree-ssa-loop-ivopts.c
	$(call spelling,classess,classes)
	$(call spelling,eroneous,erroneous)
	$(call spelling,identifer,identifier)
# This breaks the build because of a long line in Ada code
#	$(call spelling,incosistent,inconsistent)
	$(call spelling,informations,information)
	$(call spelling,interchage,interchange)
	$(call spelling,intial,initial)
	$(call spelling,intrument,instrument)
	$(call spelling,mininum,minimum)
	$(call spelling,mutiple,multiple)
	$(call spelling,occurence,occurrence)
	$(call spelling,refrence,reference)
	$(call spelling,Regsitered,Registered)
	$(call spelling,splitted,split)
	$(call spelling,Staticly,Statically)
	$(call spelling,temorary,temporary)
	$(call spelling,unsuported,unsupported)
	$(call spelling,wihout,without)
# Force /bin/sh in mkheaders
	sed -i sX@SHELL@X/bin/shX $(upstream_dir)/fixincludes/mkheaders.in
# Fix the date and time of patched files
	find . -type f -a -newermt '@$(SOURCE_DATE_EPOCH)' -print0 | \
		xargs -0r touch --no-dereference --date='@$(SOURCE_DATE_EPOCH)'
	touch $@

# 32- and 64-bit targets are split: 32-bit uses Dwarf2, 64-bit the default SEH
configure: configure-stamp
configure-stamp: debian/control mingw-w64-patch-stamp
	dh_testdir
	echo ============= Building with the following debian/control =============
	cat debian/control
	echo ======================================================================
ifeq ($(filter stage1,$(DEB_BUILD_PROFILES)),)
	set -e; \
	for threads in $(threads); do \
		echo $(base_version)-$$threads > $(upstream_dir)/gcc/BASE-VER; \
		target=$(target32); \
		dh_auto_configure \
			-B$(build_dir)/$(target32)-$$threads \
			-D$(upstream_dir) -- \
			$(CONFFLAGS) --disable-sjlj-exceptions --with-dwarf2; \
		target=$(target64); \
		dh_auto_configure \
			-B$(build_dir)/$(target64)-$$threads \
			-D$(upstream_dir) -- \
			$(CONFFLAGS); \
	done
else
	set -e; \
	target=$(target32); \
	dh_auto_configure \
		-B$(build_dir)/$(target32) \
		-D$(upstream_dir) -- \
		$(CONFFLAGS) --disable-sjlj-exceptions --with-dwarf2; \
	target=$(target64); \
	dh_auto_configure \
		-B$(build_dir)/$(target64) \
		-D$(upstream_dir) -- \
		$(CONFFLAGS)
endif
	touch $@

build: build-arch build-indep
build-arch: build-arch-stamp
build-arch-stamp: configure-stamp
	dh_testdir
ifeq ($(filter stage1,$(DEB_BUILD_PROFILES)),)
	set -e; \
	for target in $(targets); do \
		for threads in $(threads); do \
			echo $(base_version)-$$threads > $(upstream_dir)/gcc/BASE-VER && \
			cd $(build_dir)/$$target-$$threads && \
			$(MAKE) $(NJOBS) $(BUILD_TARGET); \
		done; \
	done
else
	set -e; \
	for target in $(targets); do \
		cd $(build_dir)/$$target && \
		$(MAKE) $(NJOBS) $(BUILD_TARGET); \
	done
endif
	touch $@
build-indep: build-indep-stamp
build-indep-stamp: configure-stamp
	touch $@

install-arch: install-arch-stamp
install-arch-stamp: build-arch-stamp
	dh_testdir
	dh_testroot
	dh_prep

# Base installation, move misplaced DLLs and libraries
ifeq ($(filter stage1,$(DEB_BUILD_PROFILES)),)
	set -e; \
	for target in $(targets); do \
		for threads in $(threads); do \
			echo $(base_version)-$$threads > $(upstream_dir)/gcc/BASE-VER && \
			cd $(build_dir)/$$target-$$threads && \
			$(MAKE) DESTDIR=$(top_dir)/debian/tmp $(INSTALL_TARGET); \
			if [ -f $(top_dir)/debian/tmp/usr/lib/gcc/$$target/lib/*.a ]; then \
				mv $(top_dir)/debian/tmp/usr/lib/gcc/$$target/lib/*.a $(top_dir)/debian/tmp/usr/lib/gcc/$$target/$(base_version)-$$threads/; \
			fi; \
			if [ -f $(top_dir)/debian/tmp/usr/lib/gcc/$$target/*.dll ]; then \
				mv $(top_dir)/debian/tmp/usr/lib/gcc/$$target/*.dll $(top_dir)/debian/tmp/usr/lib/gcc/$$target/$(base_version)-$$threads/; \
			fi; \
		done; \
	done
else
	set -e; \
	for target in $(targets); do \
		cd $(build_dir)/$$target && \
		$(MAKE) DESTDIR=$(top_dir)/debian/tmp $(INSTALL_TARGET); \
		if [ -f $(top_dir)/debian/tmp/usr/lib/gcc/$$target/lib/*.a ]; then \
			mv $(top_dir)/debian/tmp/usr/lib/gcc/$$target/lib/*.a $(top_dir)/debian/tmp/usr/lib/gcc/$$target/$(base_version)/; \
		fi; \
		if [ -f $(top_dir)/debian/tmp/usr/lib/gcc/$$target/*.dll ]; then \
			mv $(top_dir)/debian/tmp/usr/lib/gcc/$$target/*.dll $(top_dir)/debian/tmp/usr/lib/gcc/$$target/$(base_version)/; \
		fi; \
	done
endif

	# Remove files which conflict with other packages
	# gcc-$(target_version)-locales
	rm -rf $(top_dir)/debian/tmp/usr/share/locale
	# binutils-dev
	rm -f $(top_dir)/debian/tmp/usr/lib/libiberty.a
	# libstdc++6-$(target_version)-dbg (potentially)
	rm -rf $(top_dir)/debian/tmp/usr/share/gcc-*/python
	# -doc packages
	rm -rf $(top_dir)/debian/tmp/usr/share/info
	rm -rf $(top_dir)/debian/tmp/usr/share/man/man7
	# libcc1-0
	rm -f $(top_dir)/debian/tmp/usr/lib/libcc1*

	# No need to ship empty manpages
	rm -rf $(top_dir)/debian/tmp/usr/share/man/man1

	# Drop .la files
	find $(top_dir)/debian/tmp -name \*.la -delete

	for i in 1 2; do \
		find $(top_dir)/debian/tmp/ -type d -empty | while read i; do rmdir $$i; done; \
	done

	touch $@

binary-indep: build-indep
	dh_testdir
	dh_testroot
	dh_installdocs -i
	dh_installchangelogs -i $(upstream_dir)/ChangeLog
	dh_lintian -i
	dh_link -i
	dh_compress -i
	dh_fixperms -i
	dh_installdeb -i
	dh_gencontrol -i -- -v$(deb_version) -Vlocal:Version=$(deb_upstream_version) -Vgcc:Version=$(source_version) -Vfirstsplit:Version=$(first_split_version) -Vthreadsplit:Version=$(thread_split_version) -Vlibcaf:Version=$(libcaf_migration_version) -Vruntimesplit:Version=$(runtime_split_version)
	dh_md5sums -i
	dh_builddeb -i

binary-arch: build-arch install-arch
	dh_testdir
	dh_testroot
	for file in debian/*.in; do sed 'sX@@WARNING@@XThis file is generated using debian/rules, do not editXg;s/@@VERSION@@/$(target_version)/g;s/@@BASEVERSION@@/$(base_version)/g' < $$file > $${file%%.in}; chmod --reference=$$file $${file%%.in}; done
ifeq ($(filter $(DEB_HOST_ARCH),$(gnat_arches)),)
	sed -i /adalib/d debian/*-runtime.install
endif
	dh_install -a
	dh_installdocs -a --link-doc=gcc-mingw-w64-base
	dh_installman -a
	dh_installchangelogs -a $(upstream_dir)/ChangeLog
	dh_missing -a --list-missing
	# Strip the binaries
ifneq (,$(findstring ada,$(languages)))
	dh_strip -a -Xw64-mingw32/lib $(patsubst %,-Xw64-mingw32/$(base_version)-%/lib,$(threads)) $(patsubst %,-Xw64-mingw32/$(base_version)-%/adalib,$(threads))
else
	dh_strip -a -Xw64-mingw32/lib $(patsubst %,-Xw64-mingw32/$(base_version)-%/lib,$(threads))
endif
	# Strip the libraries
ifeq (,$(filter nostrip,$(DEB_BUILD_OPTIONS)))
	find $(top_dir)/debian -type f -name liblto_plugin.so.\*.\*.\* | xargs strip --remove-section=.comment --remove-section=.note --strip-unneeded
	for target in $(targets); do \
		find $(top_dir)/debian -path \*/$$target/\* \( \( -path \*/plugin -prune \) -o \( -type f -name lib\*.a -print \) \) | xargs -r $$target-strip --strip-unneeded; \
	done
endif
	dh_lintian -a
	dh_link -a
	dh_compress -a
	dh_fixperms -a
	# Fix permissions further
ifneq (,$(findstring ada,$(languages)))
	# *.ali files must be read-only
	find $(top_dir)/debian -name \*.ali | xargs -r chmod 444
endif
	dh_installdeb -a
	dh_shlibdeps -a
	dh_gencontrol -a -- -v$(deb_version) -Vlocal:Version=$(deb_upstream_version) -Vgcc:Version=$(source_version) -Vfirstsplit:Version=$(first_split_version) -Vthreadsplit:Version=$(thread_split_version) -Vlibcaf:Version=$(libcaf_migration_version) -Vruntimesplit:Version=$(runtime_split_version)
	dh_md5sums -a
	dh_builddeb -a

binary: binary-indep binary-arch

.PHONY: binary-indep binary-arch binary clean build-indep build-arch build install-arch configure mingw-w64-patch control
