#-*- mode: makefile; -*-
# To see available targets"
# make help

SHELL := /bin/bash

.SHELLFLAGS := -ec

VERSION := $(shell test -e VERSION || echo 1.0.0 > VERSION; cat VERSION)

MODULE_NAME  ?= $(shell SOURCE=$(pwd) perl -MCwd=abs_path -MFile::Basename=basename -e '$$m=basename(abs_path($$ENV{SOURCE})); $$m =~s/\-/::/g; print $$m')

MODULE_PATH = lib/$(shell echo $(MODULE_NAME) | perl -npe 's/::/\//g;').pm

PROJECT_NAME ?= $(shell echo $(MODULE_NAME) | sed -e 's/::/-/g;')

NO_ECHO ?= @

UNIT_TEST_NAME = $(shell TEST_NAME=$(PROJECT_NAME) perl -e 'printf q{t/00-%s.t}, lc $$ENV{TEST_NAME}')

MAKE_CPAN_DIST := $(shell command -v make-cpan-dist.pl)
SCANDEPS       := $(shell command -v scandeps-static.pl)
POD2MARKDOWN   := $(shell command -v pod2markdown)
GIT            := $(shell command -v git)
PODEXTRACT     := $(shell command -v podextract)
MD_UTILS       := $(shell command -v md-utils.pl)
BOOTSTRAPPER   := $(shell command -v bootstrapper)

GIT_NAME     ?= $(shell $(GIT) config --global user.name || echo "Anonymouse")
GIT_EMAIL    ?= $(shell $(GIT) config --global user.email || echo "anonymouse@example.org")
GITHUB_USER  ?= $(shell $(GIT) config --global user.github || echo "anonymouse")

CONFIG_READER = CPAN::Maker::Bootstrapper::ConfigReader

BASEDIR  ?= $(shell perl -M$(CONFIG_READER) -e 'print $(CONFIG_READER)->new("$(CONFIG)")->cpan_maker_basedir;')

MIN_PERL_VERSION ?= 5.010

SCAN ?= ON

define find-files
$(1) := $(patsubst %.in,%,$(shell find $(2) -type f -name "$(3)"))
endef

$(eval $(call find-files,PERL_MODULES,lib,*.pm.in))
$(eval $(call find-files,BIN_FILES,bin,*.in))
$(eval $(call find-files,TESTS,t,*.t))
$(eval $(call find-files,SOURCE_FILES,lib bin,*.p[ml].in))

POD_MODULES = $(PERL_MODULES:.pm=.pod)

TARBALL = $(PROJECT_NAME)-$(VERSION).tar.gz

.DEFAULT_GOAL := all

all: $(TARBALL) ## builds distribution tarball and dependencies

include .includes/perl.mk

bin/%.sh: bin/%.sh.in
	$(NO_ECHO)sed -e 's/[@]PACKAGE_VERSION[@]/$(VERSION)/' \
	    -e 's/[@]MODULE_NAME[@]/$(MODULE_NAME)/' < $< > $@; \
	chmod +x $@

bin/%: bin/%.in
	$(NO_ECHO)sed -e 's/[@]PACKAGE_VERSION[@]/$(VERSION)/' \
	    -e 's/[@]MODULE_NAME[@]/$(MODULE_NAME)/' < $< > $@; \
	chmod +x $@

.PHONY: quick
quick: ## quick build, turns off scanning, perltidy, perlcritic
	$(NO_ECHO)$(MAKE) SCAN=off LINT=off

cpanfile: requires test-requires 
	$(NO_ECHO)if [[ -e requires ]] && [[ -e test-requires ]]; then \
	  all_requires=$$(mktemp); trap 'rm -f $$all_requires' EXIT; \
	  cp requires $$all_requires; \
	  cat test-requires >>$$all_requires; \
	  sort -u $$all_requires | perl -ne 'chomp; s/^[+]//; ($$m,$$v)=split/\s+/,$$_,2; print qq{requires "$$m", "$$v";\n} if $$m;' >$@; \
	else \
	  echo >&2 "ERROR: make sure SCAN=on to produce requires, test-requires"; \
	fi
DEPS = \
    buildspec.yml \
    README.md \
    $(MODULE_PATH).in \
    $(PERL_MODULES) \
    $(BIN_FILES) \
    requires \
    cpanfile \
    test-requires \
    $(UNIT_TEST_NAME) \
    ChangeLog

$(TARBALL): $(DEPS) \
    $(if $(tidy_on), $(PERL_MODULES:%=%.tdy) $(PERL_BIN_FILES:%=%.tdy)) \
    $(if $(critic_on), $(PERL_MODULES:%=%.crit) $(PERL_BIN_FILES:%=%.crit))
	$(MAKE_CPAN_DIST) -b $<

module.pm.tmpl:
	$(NO_ECHO)if [[ -n "$(STUB)" ]]; then \
	  cp --preserve=all --update=none $(STUB) $@; \
	  chmod +w $@; \
	else \
	  touch $@; \
	fi; \

$(MODULE_PATH).in: | module.pm.tmpl
	$(NO_ECHO)mkdir -p $$(dirname $@); \
	test -e $@ || sed -e 's/[@]MODULE_NAME[@]/$(MODULE_NAME)/' \
	    -e 's/[@]GIT_NAME[@]/$(GIT_NAME)/' \
	    -e 's/[@]GIT_EMAIL[@]/$(GIT_EMAIL)/' < $< > $@

test.t.tmpl:
	$(NO_ECHO)template=$$(perl -MFile::ShareDir=dist_file -e 'print dist_file(q{CPAN-Maker-Bootstrapper}, q{$@});' 2>/dev/null); \
	test -n "$$template" && cp $$template $@ || touch $@; \
	chmod 0644 $$template

$(UNIT_TEST_NAME): | test.t.tmpl
	$(NO_ECHO)sed -e 's/[@]MODULE_NAME[@]/$(MODULE_NAME)/' < test.t.tmpl > $@

ifeq ($(wildcard README.md.in),)
# If README.md.in does NOT exist, use POD2MARKDOWN on the module
README.md: $(MODULE_PATH)
	$(NO_ECHO)tmpfile=$$(mktemp); \
	trap 'rm -f $$tmpfile' EXIT; \
	echo "@TOC@" > $$tmpfile; \
	$(POD2MARKDOWN) $< >> $$tmpfile; \
	$(MD_UTILS) $$tmpfile > $@;
else
# If README.md.in DOES exist, use MD_UTILS on the template
README.md: README.md.in
	$(NO_ECHO)$(MD_UTILS) $< > $@
endif

modulino.tmpl:
	$(NO_ECHO)modulino_path=$$(perl -MFile::ShareDir=dist_file -e 'print dist_file(q{CPAN-Maker-Bootstrapper}, q{modulino.tmpl});' 2>/dev/null); \
	cp $$modulino_path $@

.PHONY: modulino
modulino: modulino.tmpl ## creates a bash script that calls your modulino (MODULE_NAME=module ALIAS=name)
	$(NO_ECHO)trap 'rm -f modulino.tmpl' EXIT; \
	MODULE_NAME="$(MODULE_NAME)"; \
	ALIAS="$${ALIAS:-$$MODULE_NAME}"; \
	binfile=$$(echo "$$ALIAS" | perl -npe 's/::/\-/g;'); \
	modulino="bin/$${binfile,,}"; \
	sed -e "s/[@]MODULE_NAME[@]/$$MODULE_NAME/" \
	    -e "s/[@]ALIAS[@]/$$ALIAS/" $< > "$${modulino}.in"; \
	test -e .gitignore && { grep -q "$$modulino" .gitignore || echo "$$modulino" >> .gitignore; }; \
	echo "$$modulino"

define scan-deps
	dep_requires=$$(mktemp); \
	packages=$$(mktemp); \
	cleanfiles="$$cleanfiles $$dep_requires $$packages $(1).tmp"; \
	min_perl_version=$$(perl -MYAML=LoadFile -e 'print LoadFile(q{buildspec.yml})->{q{min-perl-version}};'); \
	if [[ -n "$$min_perl_version" ]]; then \
	  min_perl_version="-m $$min_perl_version"; \
	fi; \
	for a in $$(find $(2) -name "$(3)"); do \
	  perl -ne 'print "$$1\n" if /^package +(.*?);/' $$a >> $$packages; \
	  echo >&2 "Scanning...$$a"; \
	  $(SCANDEPS) -r $$min_perl_version --no-core $$a | awk '{printf "%s %s\n", $$1,$$2}' >> $$dep_requires; \
	done; \
	if test -s "$$dep_requires"; then \
	  sort -u $$dep_requires > $(1).tmp; \
	  grep -vFf "$$packages" "$(1).tmp" > $(1); \
	else \
	  touch $(1); \
	fi
endef

define filter_requires = 

  sub get_requires {
    my ($infile) = @_;

    return {}
      if !-s $infile;

    my %requires;

    open my $fh, '<', $infile or
      die "could not open $infile for reading\n";

    while (<$fh>) {
      chomp;
      my ($m,$v) = split ' ', $_;
      $requires{$m} = $v // 0;
    }

    close $fh;

    return \%requires;
  }

  my $skip_requires = get_requires("$ENV{REQUIRES}.skip");
  my $requires_tmp  = get_requires("$ENV{REQUIRES}.xxx");
  my $requires      = get_requires($ENV{REQUIRES});

  my %new_requires;

  # copy preserved modules (ones preceded with '+')
  foreach my $m (keys %{$requires_tmp} ) {
    next if $m !~/^\+/xsm;
    $new_requires{$m} = $requires_tmp->{$m};
  }

  foreach my $m (keys %{$requires} ) {
    # skip modules on skip list
    next if exists $skip_requires->{$m};
    next if exists $requires_tmp->{"+$m"};

    # keep modules from preserved list if versions differ (user must have specified specific version)
    if ( exists $requires_tmp->{$m} && $requires_tmp->{$m} ne $requires->{$m} ) {
      $new_requires{$m} = $requires_tmp->{$m};
    }
    else {
      $new_requires{$m} = $requires->{$m};
   }
  }

  print join q{}, map { "$_ $new_requires{$_}\n" } keys %new_requires;

endef

export s_filter_requires = $(value filter_requires)

requires: $(SOURCE_FILES) ## creates or updates the `requires` file used to populate PREQ_PM section of the Makefile.PL
	$(NO_ECHO)cleanfiles="$@.tmp $@.xxx"; \
	trap 'rm -f $$cleanfiles' EXIT; \
	scan="$(SCAN)"; \
	if [[ "$${scan^^}" = "ON" ]]; then \
	  if test -e "$@"; then \
	    cp "$@" "$@.xxx"; \
	  fi; \
	  $(call scan-deps,$@,lib bin,*.p[ml].in); \
	  if test -e "$@.xxx"; then \
	    requires_list=$$(REQUIRES="$@" perl -e "$$s_filter_requires"); \
	    echo "$$requires_list" | sort > "$@"; \
	  fi; \
	fi

test-requires: $(TESTS) ## creates or update the `test-requires` file used to populate the TEST_REQUIRES section of the Makefile.PL
	$(NO_ECHO)cleanfiles="$@.tmp $@.xxx"; \
	trap 'rm -f $$cleanfiles' EXIT; \
	scan="$(SCAN)"; \
	if [[ "$${scan^^}" = "ON" ]]; then \
	  if test -e "$@"; then \
	    cp "$@" "$@.xxx"; \
	  fi; \
	  $(call scan-deps,$@,t,*.t); \
	  if test -e "$@.xxx"; then \
	    requires_list=$$(REQUIRES="$@" perl -e "$$s_filter_requires"); \
	    echo "$$requires_list" | sort > "$@"; \
	  fi; \
	fi


ChangeLog:
	$(NO_ECHO)test -e $@ || touch $@

buildspec.yml.tmpl:
	$(NO_ECHO)template=$$(perl -MFile::ShareDir=dist_file -e 'print dist_file(q{CPAN-Maker-Bootstrapper}, q{$@});'); \
	echo $$template; \
	test -n "$$template" && cp $$template $@ || echo touch $@; \
	chmod 0644 $$template

buildspec.yml: | buildspec.yml.tmpl
	$(NO_ECHO)buildspec=$$(mktemp); \
	specfile="$(PROJECT_NAME)"; \
	specfile="$${specfile,,}.yml"; \
	if [[ -e "$$specfile" ]]; then \
	  share_files="    - $$specfile\n"; \
	fi; \
	trap 'rm -f $$buildspec' EXIT; \
	sed -e 's/[@]MODULE_NAME[@]/$(MODULE_NAME)/g' \
	    -e 's/[@]GIT_NAME[@]/$(GIT_NAME)/g' \
	    -e 's/[@]GITHUB_USER[@]/$(GITHUB_USER)/g' \
	    -e 's/[@]GIT_EMAIL[@]/$(GIT_EMAIL)/g' \
	    -e 's/[@]PROJECT_NAME[@]/$(PROJECT_NAME)/g' \
	    -e "s/[@]EXTRA_FILES[@]/$$extra_files/g" \
	    -e "s/[@]SHARE_FILES[@]/$$share_files/g" \
	    -e 's/[@]MIN_PERL_VERSION[@]/$(MIN_PERL_VERSION)/g' buildspec.yml.tmpl > $$buildspec; \
	if test -e resources.yml; then \
	  cat resources.yml >> $$buildspec; \
	  rm resources.yml; \
	fi; \
	cp $$buildspec $@;

include .includes/git.mk
include .includes/help.mk
include .includes/release-notes.mk
include .includes/update.mk
include .includes/upgrade.mk
include .includes/version.mk

CLEANFILES += \
    $(BIN_FILES) \
    $(PERL_MODULES) \
    $(POD_MODULES) \
    *.tar.gz \
    *.tmp \
    *.xxx \
    extra-files \
    provides \
    module.pm.tmpl \
    release-*.{lst,diffs}

clean: ## removes temporary build artifacts
	$(NO_ECHO)rm -f $(CLEANFILES)

.PHONY: basedir
basedir:
	$(NO_ECHO)echo $(BASEDIR)
