# Makefile for Netpbm

# Configuration should normally be done in the included file config.mk.

# Targets in this file:
#
#   nonmerge:     Build everything, in the source directory.
#   merge:        Build everything as merged executables, in the source dir.
#   package:      Make a package of Netpbm files ready to install.
#
#   deb:          Make a .deb file in the current dir.
#
#   check-tree:     Conduct tests on Netpbm files in the source dir. 
#                   with "target=pamtotga" tests only pamtotga.
#   check-package:  Conduct tests on packaged Netpbm files.
#   check-install:  Conduct tests on installed Netpbm files.
#   check:          Default check.  Synonym for check-package.
#
#   clean:        Delete target executables and intermediate objects.
#   distclean:    Delete configuration files in addition to the above.
#
#   tags:         Generate/update an Emacs tags file, named TAGS.
#   
#   The default target is either "merge" or "nonmerge", as determined by
#   the DEFAULT_TARGET variable set by config.mk.

# Debugging techniques:
#
#   When a test (make check ...) fails, the incorrect output is in the file
#   TESTNAME.out in /tmp/netpbm-test/ .
#
#   To build so you can easily debug with a debugger such as Gdb:
#
#     make CFLAGS="-g -O0"
#
#   To debug libnetpbm, link it statically into the using program:
#
#     make NETPBMLIBTYPE=unixstatic pamcut
#
#     (Then 'ldd ./pamcut' to make sure it worked)
#
# About the "merge" target: Normally the Makefiles build separate
# executables for each program.  However, on some systems (especially
# those without shared libraries) this can mean a lot of space.  In
# this case you might try building a "merge" instead.  The idea here
# is to link all the programs together into one huge executable, along
# with a tiny dispatch program that runs one of the programs based on
# the command name with which it was invoked.  You install the merged
# executable with a file system link for the name of each program it
# includes.  This is much more important when you're statically
# linking than when you're using shared libraries.  On a Sun3 under
# SunOS 3.5, where shared libraries are not available, the space for
# executables went from 2970K to 370K in an older Netpbm.  On a
# GNU/Linux IA32 system with shared libraries in 2002, it went from
# 2949K to 1663K.

# To build a "merge" system, just set DEFAULT_TARGET to "merge" instead
# of "nomerge" in config.mk.  In that case, you should probably also
# set NETPBMLIBTYPE to "unixstatic", since a shared library doesn't do you 
# much good.

# The CURDIR variable presents a problem because it was introduced in
# GNU Make 3.77.  We need the CURDIR variable in order for our 'make
# -C xxx -f xxx' commands to work.  If we used the obvious alternative
# ".", that wouldn't work because it would refer to the directory
# named in -C, not the directory the make file you are reading is
# running in.  The -f option is necessary in order to have separate
# source and object directories.

# For the check targets, results go in a results directory, which you'll need
# if a test fails to find out how it failed.  The default is
# RESULTDIR_DEFAULT in config.mk.  Override it with
# 'make resultdir=/tmp/myresultdir .

ifeq ($(CURDIR)x,x)
all package install:
	@echo "YOU NEED AT LEAST VERSION 3.77 OF GNU MAKE TO BUILD NETPBM."
	@echo "Netpbm's makefiles need the CURDIR variable that was "
	@echo "introduced in 3.77.  Your version does not have CURDIR."
	@echo
	@echo "You can get a current GNU Make via http://www.gnu.org/software"
	@echo 
	@echo "If upgrading is impossible, try modifying GNUMakefile and "
	@echo "common.mk to replace \$(CURDIR) with \$(shell /bin/pwd) "
else


# srcdir.mk defines SRCDIR .
include srcdir.mk
BUILDDIR = $(CURDIR)
SUBDIR = 
VPATH=.:$(SRCDIR)

include $(BUILDDIR)/config.mk

PROG_SUBDIRS = converter analyzer editor generator other
PRODUCT_SUBDIRS = lib $(PROG_SUBDIRS)
SUPPORT_SUBDIRS = urt icon buildtools test

SUBDIRS = $(PRODUCT_SUBDIRS) $(SUPPORT_SUBDIRS)

SCRIPTS = manweb
MANUALS1 = netpbm
NOMERGEBINARIES = netpbm

OBJECTS = netpbm.o

default: $(DEFAULT_TARGET)
	echo "EXISTENCE OF THIS FILE MEANS NETPBM HAS BEEN BUILT." \
	  >build_complete
	@echo ""
	@echo "Netpbm is built.  The next step is normally to package it "
	@echo "for installation by running "
	@echo ""
	@echo "    make package pkgdir=DIR"
	@echo ""
	@echo "to copy all the Netpbm files you need to install into the "
	@echo "directory DIR.  Then you can proceed to install."

all: nonmerge

.PHONY: nonmerge
nonmerge: $(PRODUCT_SUBDIRS:%=%/all)

# Completely parallel make (make --jobs) does not work because there are
# multiple targets somewhere in the Netpbm build that depend upon pm_config.h
# and similar targets, and the threads building those multiple targets might
# simultaneously find that pm_config.h needs to be built and proceed to build
# it.  After one thread has built pm_config.h, it will proceed to use
# pm_config.h, but the other thread is still building pm_config.h, which is
# not valid while it is in the middle of being used.
#
# But many submakes don't have any such shared dependencies, so build their
# targets in parallel just fine.  So we declare this make file ineligible for
# parallel make and have special dependencies to get pm_config.h and like
# targets built before any submakes begin.  The submakes will thus never find
# that pm_config.h needs to be built, so we leave them eligible for parallel
# make.

.NOTPARALLEL:

$(SUBDIRS:%=%/all) lib/util/all: pm_config.h inttypes_netpbm.h version.h
$(PROG_SUBDIRS:%=%/all): lib/all $(SUPPORT_SUBDIRS:%=%/all)
lib/all: lib/util/all
netpbm: lib/all

.PHONY: lib/util/all
lib/util/all:
	mkdir -p lib/util
	$(MAKE) -C lib/util -f $(SRCDIR)/lib/util/Makefile \
	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) all

OMIT_CONFIG_RULE = 1
OMIT_VERSION_H_RULE = 1
OMIT_INTTYPES_RULE = 1
include $(SRCDIR)/common.mk

$(BUILDDIR)/config.mk: $(SRCDIR)/config.mk.in
	$(SRCDIR)/configure $(SRCDIR)/config.mk.in


# typegen is a utility program used by the make file below.
TYPEGEN = $(BUILDDIR)/buildtools/typegen

# endiangen is a utility program used by the make file below.
ENDIANGEN = $(BUILDDIR)/buildtools/endiangen

$(TYPEGEN) $(ENDIANGEN): $(BUILDDIR)/buildtools
	$(MAKE) -C $(dir $@) -f $(SRCDIR)/buildtools/Makefile \
	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 

inttypes_netpbm.h: $(TYPEGEN)
	$(TYPEGEN) >$@


# testrandom is a utility program used by the make file below.
TESTRANDOM = $(BUILDDIR)/test/testrandom

$(TESTRANDOM): $(BUILDDIR)/test
	$(MAKE) -C $(dir $@) -f $(SRCDIR)/test/Makefile \
	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 

# We run a couple of programs on the build machine in computing the
# contents of pm_config.h.  We need to give the user a way not to do
# that or to override the results, because it doesn't work if he's
# cross compiling.

pm_config.h: \
  $(SRCDIR)/pm_config.in.h config.mk inttypes_netpbm.h \
  $(ENDIANGEN)
# Note that this rule depends on the effect of the .DELETE_ON_ERROR
# target we get from common.mk
	echo '/* pm_config.h GENERATED BY A MAKE RULE */' >$@
	echo '#ifndef PM_CONFIG_H' >>$@
	echo '#define PM_CONFIG_H' >>$@
ifeq ($(INTTYPES_H)x,x)
	echo '/* Dont need to #include any inttypes.h-type thing */' >>$@
else
  ifeq ($(INTTYPES_H),"inttypes_netpbm.h")
	cat inttypes_netpbm.h >>$@
  else
	echo '#include $(INTTYPES_H)' >>$@
  endif
endif
ifeq ($(HAVE_INT64),Y)
	echo "#define HAVE_INT64 1" >>$@
else
	echo "#define HAVE_INT64 0" >>$@
endif	
ifeq ($(WANT_SSE),Y)
	echo "#define WANT_SSE 1" >>$@
else
	echo "#define WANT_SSE 0" >>$@
endif	
ifeq ($(DONT_HAVE_PROCESS_MGMT),Y)
	echo "#define HAVE_FORK 0" >>$@
else
	echo "#define HAVE_FORK 1" >>$@
endif
	echo '#define RGB_DB_PATH "$(RGB_DB_PATH)"' >>$@
	echo '/* pm_config.h.in FOLLOWS ... */' >>$@
	cat $(SRCDIR)/pm_config.in.h >>$@
	$(ENDIANGEN) >>$@
	echo '#endif' >>$@


MAJOR := $(NETPBM_MAJOR_RELEASE)
MINOR := $(NETPBM_MINOR_RELEASE)
POINT := $(NETPBM_POINT_RELEASE)
version.h: $(SRCDIR)/version.mk
	@rm -f $@
	@echo "/* Generated by make file rule */" >>$@
	@echo "#define NETPBM_VERSION" \
	  \"Netpbm $(MAJOR).$(MINOR).$(POINT)"\"" >>$@


.PHONY: install
install:
	@echo "After doing a 'make', do "
	@echo ""
	@echo "  make package pkgdir=DIR"
	@echo ""
	@echo "to copy all the Netpbm files you need to install into the "
	@echo "directory DIR."
	@echo ""
	@echo "Then, do "
	@echo ""
	@echo "  ./installnetpbm"
	@echo
	@echo "to install from there to your system via an interactive.  "
	@echo "dialog.  Or do it manually using simple copy commands and "
	@echo "following instructions in the file DIR/README"

.PHONY: package package_build init_package advise_installnetpbm
package: build_complete package_build advise_installnetpbm

build_complete:
# The regular build creates this file as its last act, so if it doesn't exist,
# that means either the user skipped the build step, or the build failed.
	@echo "You must build Netpbm before you can package Netpbm. "
	@echo "The usual way to do this is to type 'make' with no arguments."
	@echo "If you did that, then the build apparently failed.  There "
	@echo "should have been error messages indicating why.  If you "
	@echo "can't fix the build problem, you can do 'make --keep-going' "
	@echo "to force the build to continue with other parts that "
	@echo "it may be able to build successfully, then do "
	@echo "'make package --keep-going' to package whatever was "
	@echo "successfully built."
	@echo
	@false;

package_build: init_package install-run install-dev 

MAJOR=$(NETPBM_MAJOR_RELEASE)
MINOR=$(NETPBM_MINOR_RELEASE)
POINT=$(NETPBM_POINT_RELEASE)

init_package:
	@if [ -d $(PKGDIR) ]; then \
	  echo "Directory $(PKGDIR) already exists.  Please specify a "; \
	  echo "directory that can be created fresh, like this: "; \
	  echo "  make package pkgdir=/tmp/newnetpbm "; \
	  false; \
	  fi
	mkdir $(PKGDIR)
	echo "Netpbm install package made by 'make package'" \
	    >$(PKGDIR)/pkginfo
	date >>$(PKGDIR)/pkginfo
# For many years, the version string was "Netpbm x.y.z" instead of just
# "x.y.z".  This was probably related to the fact that when you ask a Netpbm
# program its version, it says "Netpbm x.y.z" to let you know it's the version
# of the whole package, not just that program.  And also to distinguish from
# the Pbmplus version of the program.  But this is highly unconventional in
# pkg-config or config files, so we removed the "Netpbm" in Netpbm 10.90
# (March 2020).
	echo $(MAJOR).$(MINOR).$(POINT) >$(PKGDIR)/VERSION
	$(INSTALL) -c -m 664 $(SRCDIR)/buildtools/README.pkg $(PKGDIR)/README
	$(INSTALL) -c -m 664 $(SRCDIR)/buildtools/config_template \
	  $(PKGDIR)/config_template
	$(INSTALL) -c -m 664 $(SRCDIR)/buildtools/pkgconfig_template \
	  $(PKGDIR)/pkgconfig_template

advise_installnetpbm:
	@echo
	@echo "Netpbm has been successfully packaged under directory"
	@echo "$(PKGDIR).  Run 'installnetpbm' to install it on your system."

.PHONY: install-run
ifeq ($(DEFAULT_TARGET),merge)
install-run: install-merge
else
install-run: install-nonmerge 
endif

.PHONY: install-merge install-nonmerge
install-merge: install.merge install.lib install.data

install-nonmerge: install.bin install.lib install.data

.PHONY: merge
merge: lib/all netpbm

MERGELIBS = 
ifneq ($(ZLIB),NONE)
  MERGELIBS += $(ZLIB)
endif
ifneq ($(JPEGLIB),NONE)
  MERGELIBS += $(JPEGLIB)
endif
ifneq ($(TIFFLIB),NONE)
  MERGELIBS += $(TIFFLIB)
endif
ifneq ($(URTLIB),NONE)
  MERGELIBS += $(URTLIB)
endif
ifneq ($(LINUXSVGALIB),NONE)
  MERGELIBS += $(LINUXSVGALIB)
endif

ifneq ($(shell $(PKG_CONFIG) --modversion libpng$(PNGVER)),)
  PNGLD = $(shell $(PKG_CONFIG) --libs libpng$(PNGVER))
else
  ifneq ($(shell libpng$(PNGVER)-config --version),)
    PNGLD = $(shell libpng$(PNGVER)-config --ldflags)
  else
    PNGLD = $(shell $(LIBOPT) $(LIBOPTR) $(PNGLIB) $(ZLIB))
  endif
endif

ifneq ($(shell $(PKG_CONFIG) --modversion libxml-2.0),)
  XML2LD=$(shell $(PKG_CONFIG) --libs libxml-2.0)
else
  ifneq ($(shell xml2-config --version),)
    XML2LD=$(shell xml2-config --libs)
  else
    XML2LD=
  endif
endif

ifneq ($(shell $(PKG_CONFIG) x11 --libs),)
  X11LD = $(shell $(PKG_CONFIG) x11 --libs)
else
  X11LD = $(shell $(LIBOPT) $(LIBOPTR) $(X11LIB))
endif



# If URTLIB is BUNDLED_URTLIB, then we're responsible for building it, which
# means it needs to be a dependency:
ifeq ($(URTLIB),$(BUNDLED_URTLIB))
  URTLIBDEP = $(URTLIB)
endif

# We have two different ways to do the merge build:
#  
#   1) Each directory produces an object file merge.o containing all the code
#      in that directory and its descendants that needs to go into the 'netpbm'
#      program.  The make files do this recursively, via a link command that
#      combines multiple relocatable object files into one.  All we do here
#      at the top level is make merge.o and link it with netpbm.o and the
#      libraries.
#
#      This is the clean way, and we use it whenever we can.  But we don't
#      know how to do the link on every platform.
#
#   2) Each directory produces a list of all the object files in that 
#      directory and its descendants that need to go into the 'netpbm'
#      program.  This list is in a file called 'mergelist'.  The make files
#      do this recursively.  Here at the top level, we make mergelist and
#      then do one large link of everything listed in it, plus netpbm.o and
#      the libraries.
#
#      This doesn't require any special link command like (1), but is
#      not very clean.  The dependencies don't work right.  And at least
#      one linker (on DJGPP) can't handle that many input files.

ifeq ($(LDRELOC),NONE)
  OBJECT_DEP = mergelist
  OBJECT_LIST = `cat mergelist`
else
  OBJECT_DEP = merge.o
  OBJECT_LIST = merge.o
endif

netpbm:%:%.o $(OBJECT_DEP) $(NETPBMLIB) $(URTLIBDEP) $(LIBOPT)
# Note that LDFLAGS might contain -L options, so order is important.
	$(LD) -o $@ $< $(OBJECT_LIST) \
          $(LDFLAGS) $(shell $(LIBOPT) $(NETPBMLIB) $(MERGELIBS)) \
	  $(PNGLD) $(XML2LD) $(X11LD) $(MATHLIB) $(NETWORKLD) $(LADD)

netpbm.o: mergetrylist

ifneq ($(NETPBMLIBTYPE),unixstatic)
install.lib: lib/install.lib
else
install.lib:
endif

.PHONY: install-dev
# Note that you might install the development package and NOT the runtime
# package.  If you have a special system for building stuff, maybe for 
# multiple platforms, that's what you'd do.  Ergo, install.lib is here even
# though it is also part of the runtime install.
install-dev: install.hdr install.staticlib install.lib install.sharedlibstub

.PHONY: install.hdr
install.hdr: lib/install.hdr $(PKGDIR)/include/netpbm
	$(INSTALL) -c -m $(INSTALL_PERM_HDR) \
	    $(BUILDDIR)/pm_config.h $(PKGDIR)/include/netpbm

.PHONY: lib/install.hdr
lib/install.hdr:
	$(MAKE) -C $(dir $@) -f $(SRCDIR)/lib/Makefile \
	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@)

ifeq ($(STATICLIB_TOO),Y)
BUILD_STATIC = Y
else
  ifeq ($(NETPBMLIBTYPE),unixstatic)
    BUILD_STATIC = Y
  else
    BUILD_STATIC = N
  endif
endif

.PHONY: install.staticlib
install.staticlib: 
ifeq ($(BUILD_STATIC),Y)
	$(MAKE) -C lib -f $(SRCDIR)/lib/Makefile \
	SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) install.staticlib 
endif

.PHONY: install.sharedlibstub
install.sharedlibstub:
	$(MAKE) -C lib -f $(SRCDIR)/lib/Makefile \
	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) install.sharedlibstub 

# Make the 'deb' target after making 'package'.  It generates a .deb
# file in the current directory.
.PHONY: deb
deb:
	buildtools/debian/mkdeb --buildtools=buildtools --pkgdir=$(PKGDIR)


.PHONY: check
.PHONY: check-tree
.PHONY: check-package
.PHONY: check-install

# Variables from the make env we pass down to the test scripts.
CHECK_VARS = \
	BUILDDIR=$(BUILDDIR) \
	RGBDEF=$(RGBDEF) \
	PALMMAPDIR=$(PALMMAPDIR) \
	BUILD_FIASCO=$(BUILD_FIASCO) \
	JASPERLIB="$(JASPERLIB)" \
	JBIGLIB="$(JBIGLIB)" \
	JPEGLIB="$(JPEGLIB)" \
	LINUXSVGALIB="$(LINUXSVGALIB)" \
	PNGLIB="$(PNGLIB)" \
	TIFFLIB="$(TIFFLIB)" \
	URTLIB="$(URTLIB)" \
	X11LIB="$(X11LIB)" \
	XML2_LIBS="$(XML2_LIBS)" \
	LEX="$(LEX)" \
	ZLIB="$(ZLIB)" \

# Test files in source tree.
# BUILDBINDIRS is a list of directories which contain target binaries

check-tree : BUILDBINDIRS :=./analyzer \
./converter/other \
./converter/other/cameratopam \
./converter/other/fiasco \
./converter/other/jbig \
./converter/other/jpeg2000 \
./converter/other/pamtosvg \
./converter/other/pnmtopalm \
./converter/pbm \
./converter/pbm/pbmtoppa \
./converter/pgm \
./converter/ppm \
./converter/ppm/ppmtompeg \
./converter/ppm \
./editor \
./editor/pamflip \
./editor/specialty \
./generator \
./generator/pamtris \
./other \
./other/pamx

check-tree : SRCBINDIRS :=./converter/ \
./converter/other/ \
./converter/ppm/hpcdtoppm/ \
./editor \
./generator \
./other \
./

# Create colon-separated PATH list from the above.
# Use realpath function (appears in GNU Make v.3.81) if available.

# Kludge to test whether realpath is available:
ifeq ($(realpath $(CURDIR)/.),$(CURDIR))
  check-tree : RBINDIRS :=$(sort \
     $(foreach dir,$(BUILDBINDIRS),$(realpath $(BUILDDIR)/$(dir))) \
     $(foreach dir,$(SRCBINDIRS),$(realpath $(SRCDIR)/$(dir))))
else
  check-tree : RBINDIRS :=$(sort \
     $(foreach dir,$(BUILDBINDIRS),$(BUILDDIR)/$(dir)) \
     $(foreach dir,$(SRCBINDIRS),$(SRCDIR)/$(dir)))
endif

# Kludge to express characters given special meanings by GNU Make.
# See GNU Make texinfo manual "Function Call Syntax".
empty :=
space := $(empty) $(empty)
colon :=:

check-tree : PBM_TEST_PATH := $(subst $(space),$(colon),$(strip $(RBINDIRS)))
check-tree : PBM_LIBRARY_PATH ?= $(BUILDDIR)/lib
check-tree : RGBDEF ?= $(SRCDIR)/lib/rgb.txt
check-tree : PALMMAPDIR ?= $(SRCDIR)/converter/other/pnmtopalm


# Create RESULTDIR.
# If it already exists, rename and convert to an archive directory.
# Use numbered backup.
# Note: Renaming fails with old versions of mv which do not have -T.  

resultdir-backup: FORCE
	if [ -d $(RESULTDIR) ]; \
	   then mv -T --backup=numbered $(RESULTDIR) $(RESULTDIR).bak; \
	fi; \
	mkdir -p $(RESULTDIR); \

.PHONY: testdir test

testdir:
	$(MAKE) -C test

check-tree: testdir resultdir-backup
	cd $(RESULTDIR); \
	  $(CHECK_VARS) \
	  CHECK_TYPE=tree \
	  PBM_TEST_PATH=$(PBM_TEST_PATH) \
	  LD_LIBRARY_PATH=$(PBM_LIBRARY_PATH):${LD_LIBRARY_PATH} \
	  $(SRCDIR)/test/Execute-Tests 2>&1

# Execute-Tests needs to know BUILDDIR in order to locate testrandom.
# This applies to all check varieties.

# Check after the packaging stage
# This works on typical Linux systems.
# This is the default check.

check-package : PBM_TEST_PATH := $(PKGDIR)/bin
check-package : PBM_LIBRARY_PATH := $(PKGDIR)/lib
check-package : RGBDEF ?= $(PKGDIR)/misc/rgb.txt
check-package : PALMMAPDIR ?= $(PKGDIR)/misc/
check: check-package

check-package: testdir resultdir-backup
	cd $(RESULTDIR); \
	  $(CHECK_VARS) \
	  CHECK_TYPE=package \
	  PBM_TEST_PATH=$(PBM_TEST_PATH) \
	  LD_LIBRARY_PATH=$(PBM_LIBRARY_PATH):${LD_LIBRARY_PATH} \
	  $(SRCDIR)/test/Execute-Tests 2>&1


# Check after install
check-install: PALMMAPDIR ?= ""
check-install: testdir resultdir-backup
	cd $(RESULTDIR); \
	  $(CHECK_VARS) \
	  CHECK_TYPE=install \
	  $(SRCDIR)/test/Execute-Tests 2>&1



clean: localclean

.PHONY: localclean
localclean:
	rm -f netpbm build_started build_complete
	rm -f pm_config.h inttypes_netpbm.h version.h
	rm -f *.deb

# Note that removing config.mk must be the last thing we do,
# because no other makes will work after that is done.
distclean: localdistclean
.PHONY: localdistclean
localdistclean: localclean
	-rm -f `find -type l`
	-rm -f TAGS
	-rm -f config.mk

# 'tags' generates/updates an Emacs tags file, named TAGS, in the current
# directory.  Use with Emacs command 'find-tag'.

.PHONY: tags
tags:
	find . -name "*.c" -o -name "*.h" -o -name "*.cpp" -o -name "*.hpp" | \
	  etags -

# The following endif is for the else block that contains virtually the
# whole file, for the test of the existence of CURDIR.
endif
