diff --git a/externals/loaders/libdir/Makefile b/externals/loaders/libdir/Makefile index 872ec691282f654f0d9a0e5c2947d3063cda5403..07d3bd2b62d756e518e0e15390898d129f5017d1 100644 --- a/externals/loaders/libdir/Makefile +++ b/externals/loaders/libdir/Makefile @@ -1,295 +1,33 @@ -## Pd library template version 1.0.3 -# For instructions on how to use this template, see: -# http://puredata.info/docs/developer/MakefileTemplate -LIBRARY_NAME = libdir +#!/usr/bin/make -f +# Makefile to the 'libdir' library for Pure Data. +# Needs Makefile.pdlibbuilder as helper makefile for platform-dependent build +# settings and rules (https://github.com/pure-data/pd-lib-builder). -# add your .c source files, one object per file, to the SOURCES -# variable, help files will be included automatically -SOURCES = libdir.c +lib.name = libdir -# list all pd objects (i.e. myobject.pd) files here, and their helpfiles will -# be included automatically -PDOBJECTS = +# special file that does not provide a class +lib.setup.sources = -# example patches and related files, in the 'examples' subfolder -EXAMPLES = +# all other C and C++ files in subdirs are source files per class +# (alternatively, enumerate them by hand) +class.sources = libdir.c -# manuals and related files, in the 'manual' subfolder -MANUAL = +datafiles = \ +TODO.txt \ +LICENSE.txt \ +README.txt \ +libdir-meta.pd -# if you want to include any other files in the source and binary tarballs, -# list them here. This can be anything from header files, test patches, -# documentation, etc. README.txt and LICENSE.txt are required and therefore -# automatically included -EXTRA_DIST = TODO.txt +datadirs = -# NOTE: removed help patch requirement, since this is a loader +cflags = -DVERSION='"$(lib.version)"' -#------------------------------------------------------------------------------# -# -# things you might need to edit if you are using other C libraries -# -#------------------------------------------------------------------------------# +################################################################################ +### pdlibbuilder ############################################################### +################################################################################ -CFLAGS = -DPD -I"$(PD_INCLUDE)" -Wall -W -g -LDFLAGS = -LIBS = - -#------------------------------------------------------------------------------# -# -# you shouldn't need to edit anything below here, if we did it right :) -# -#------------------------------------------------------------------------------# - -# get library version from meta file -LIBRARY_VERSION = $(shell sed -n 's|^\#X text [0-9][0-9]* [0-9][0-9]* VERSION \(.*\);|\1|p' $(LIBRARY_NAME)-meta.pd) - -CFLAGS += -DVERSION='"$(LIBRARY_VERSION)"' - -PD_INCLUDE = $(PD_PATH)/include -# where to install the library, overridden below depending on platform -prefix = /usr/local -libdir = $(prefix)/lib -pkglibdir = $(libdir)/pd-externals -objectsdir = $(pkglibdir) - -INSTALL = install -INSTALL_PROGRAM = $(INSTALL) -p -m 644 -INSTALL_DATA = $(INSTALL) -p -m 644 -INSTALL_DIR = $(INSTALL) -p -m 755 -d - -ALLSOURCES := $(SOURCES) $(SOURCES_android) $(SOURCES_cygwin) $(SOURCES_macosx) \ - $(SOURCES_iphoneos) $(SOURCES_linux) $(SOURCES_windows) - -DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) -ORIGDIR=pd-$(LIBRARY_NAME)_$(LIBRARY_VERSION) - -UNAME := $(shell uname -s) -ifeq ($(UNAME),Darwin) - CPU := $(shell uname -p) - ifeq ($(CPU),arm) # iPhone/iPod Touch - SOURCES += $(SOURCES_iphoneos) - EXTENSION = pd_darwin - OS = iphoneos - PD_PATH = /Applications/Pd-extended.app/Contents/Resources - IPHONE_BASE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin - CC=$(IPHONE_BASE)/gcc - CPP=$(IPHONE_BASE)/cpp - CXX=$(IPHONE_BASE)/g++ - ISYSROOT = -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk - IPHONE_CFLAGS = -miphoneos-version-min=3.0 $(ISYSROOT) -arch armv6 - OPT_CFLAGS = -fast -funroll-loops -fomit-frame-pointer - CFLAGS := $(IPHONE_CFLAGS) $(OPT_CFLAGS) $(CFLAGS) - LDFLAGS += -arch armv6 -bundle -undefined dynamic_lookup $(ISYSROOT) - LIBS += -lc - STRIP = strip -x - DISTBINDIR=$(DISTDIR)-$(OS) - else # Mac OS X - SOURCES += $(SOURCES_macosx) - EXTENSION = pd_darwin - OS = macosx - PD_PATH = /Applications/Pd-extended.app/Contents/Resources - OPT_CFLAGS = -ftree-vectorize -ftree-vectorizer-verbose=2 -fast -# build universal 32-bit on 10.4 and 32/64 on newer - ifeq ($(shell uname -r | sed 's|\([0-9][0-9]*\)\.[0-9][0-9]*\.[0-9][0-9]*|\1|'), 8) - FAT_FLAGS = -arch ppc -arch i386 -mmacosx-version-min=10.4 - else - FAT_FLAGS = -arch ppc -arch i386 -arch x86_64 -mmacosx-version-min=10.4 - SOURCES += $(SOURCES_iphoneos) - endif - CFLAGS += $(FAT_FLAGS) -fPIC -I/sw/include - LDFLAGS += $(FAT_FLAGS) -bundle -undefined dynamic_lookup -L/sw/lib - # if the 'pd' binary exists, check the linking against it to aid with stripping - LDFLAGS += $(shell test -e $(PD_PATH)/bin/pd && echo -bundle_loader $(PD_PATH)/bin/pd) - LIBS += -lc - STRIP = strip -x - DISTBINDIR=$(DISTDIR)-$(OS) -# install into ~/Library/Pd on Mac OS X since /usr/local isn't used much - pkglibdir=$(HOME)/Library/Pd - endif -endif -ifeq ($(UNAME),Linux) - CPU := $(shell uname -m) - SOURCES += $(SOURCES_linux) - EXTENSION = pd_linux - OS = linux - PD_PATH = /usr - OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer - CFLAGS += -fPIC - LDFLAGS += -Wl,--export-dynamic -shared -fPIC - LIBS += -lc - STRIP = strip --strip-unneeded -R .note -R .comment - DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m) -endif -ifeq (CYGWIN,$(findstring CYGWIN,$(UNAME))) - CPU := $(shell uname -m) - SOURCES += $(SOURCES_cygwin) - EXTENSION = dll - OS = cygwin - PD_PATH = $(cygpath $(PROGRAMFILES))/pd - OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer - CFLAGS += - LDFLAGS += -Wl,--export-dynamic -shared -L"$(PD_PATH)/src" -L"$(PD_PATH)/bin" - LIBS += -lc -lpd - STRIP = strip --strip-unneeded -R .note -R .comment - DISTBINDIR=$(DISTDIR)-$(OS) -endif -ifeq (MINGW,$(findstring MINGW,$(UNAME))) - CPU := $(shell uname -m) - SOURCES += $(SOURCES_windows) - EXTENSION = dll - OS = windows - PD_PATH = $(shell cd "$(PROGRAMFILES)"/pd && pwd) - OPT_CFLAGS = -O3 -funroll-loops -fomit-frame-pointer - CFLAGS += -mms-bitfields - LDFLAGS += -s -shared -Wl,--enable-auto-import - LIBS += -L"$(PD_PATH)/src" -L"$(PD_PATH)/bin" -L"$(PD_PATH)/obj" -lpd -lwsock32 -lkernel32 -luser32 -lgdi32 - STRIP = strip --strip-unneeded -R .note -R .comment - DISTBINDIR=$(DISTDIR)-$(OS) -endif - -# in case somebody manually set the HELPPATCHES above -HELPPATCHES ?= $(SOURCES:.c=-help.pd) $(PDOBJECTS:.pd=-help.pd) - -CFLAGS += $(OPT_CFLAGS) - - -.PHONY = install libdir_install single_install install-doc install-exec install-examples install-manual clean dist etags $(LIBRARY_NAME) - -all: $(SOURCES:.c=.$(EXTENSION)) - -%.o: %.c - $(CC) $(CFLAGS) -o "$*.o" -c "$*.c" - -%.$(EXTENSION): %.o - $(CC) $(LDFLAGS) -o "$*.$(EXTENSION)" "$*.o" $(LIBS) - chmod a-x "$*.$(EXTENSION)" - -# this links everything into a single binary file -$(LIBRARY_NAME): $(SOURCES:.c=.o) $(LIBRARY_NAME).o - $(CC) $(LDFLAGS) -o $(LIBRARY_NAME).$(EXTENSION) $(SOURCES:.c=.o) $(LIBRARY_NAME).o $(LIBS) - chmod a-x $(LIBRARY_NAME).$(EXTENSION) - -install: libdir_install - -# The meta and help files are explicitly installed to make sure they are -# actually there. Those files are not optional, then need to be there. -libdir_install: $(SOURCES:.c=.$(EXTENSION)) install-doc install-examples install-manual - $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) - $(INSTALL_DATA) $(LIBRARY_NAME)-meta.pd \ - $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) - test -z "$(strip $(SOURCES))" || (\ - $(INSTALL_PROGRAM) $(SOURCES:.c=.$(EXTENSION)) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) && \ - $(STRIP) $(addprefix $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/,$(SOURCES:.c=.$(EXTENSION)))) - test -z "$(strip $(PDOBJECTS))" || \ - $(INSTALL_DATA) $(PDOBJECTS) \ - $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) - -# install library linked as single binary -single_install: $(LIBRARY_NAME) install-doc install-exec - $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) - $(INSTALL_PROGRAM) $(LIBRARY_NAME).$(EXTENSION) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) - $(STRIP) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/$(LIBRARY_NAME).$(EXTENSION) - -install-doc: - $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) -# test -z "$(strip $(SOURCES) $(PDOBJECTS))" || \ -# $(INSTALL_DATA) $(HELPPATCHES) \ -# $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) - $(INSTALL_DATA) README.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/README.txt - $(INSTALL_DATA) LICENSE.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/LICENSE.txt - -install-examples: - test -z "$(strip $(EXAMPLES))" || \ - $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples && \ - for file in $(EXAMPLES); do \ - $(INSTALL_DATA) examples/$$file $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples; \ - done - -install-manual: - test -z "$(strip $(MANUAL))" || \ - $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/manual && \ - for file in $(MANUAL); do \ - $(INSTALL_DATA) manual/$$file $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/manual; \ - done - - -clean: - -rm -f -- $(SOURCES:.c=.o) $(SOURCES_LIB:.c=.o) - -rm -f -- $(SOURCES:.c=.$(EXTENSION)) - -rm -f -- $(LIBRARY_NAME).o - -rm -f -- $(LIBRARY_NAME).$(EXTENSION) - -distclean: clean - -rm -f -- $(DISTBINDIR).tar.gz - -rm -rf -- $(DISTBINDIR) - -rm -f -- $(DISTDIR).tar.gz - -rm -rf -- $(DISTDIR) - -rm -f -- $(ORIGDIR).tar.gz - -rm -rf -- $(ORIGDIR) - - -$(DISTBINDIR): - $(INSTALL_DIR) $(DISTBINDIR) - -$(DISTDIR): - $(INSTALL_DIR) $(DISTDIR) - -$(ORIGDIR): - $(INSTALL_DIR) $(ORIGDIR) - -dist: $(DISTDIR) - $(INSTALL_DATA) Makefile $(DISTDIR) - $(INSTALL_DATA) README.txt $(DISTDIR) - $(INSTALL_DATA) LICENSE.txt $(DISTDIR) - $(INSTALL_DATA) $(LIBRARY_NAME)-meta.pd $(DISTDIR) - test -z "$(strip $(ALLSOURCES))" || \ - $(INSTALL_DATA) $(ALLSOURCES) $(DISTDIR) - test -z "$(strip $(PDOBJECTS))" || \ - $(INSTALL_DATA) $(PDOBJECTS) $(DISTDIR) -# test -z "$(strip $(HELPPATCHES))" || \ - $(INSTALL_DATA) $(HELPPATCHES) $(DISTDIR) - test -z "$(strip $(EXTRA_DIST))" || \ - $(INSTALL_DATA) $(EXTRA_DIST) $(DISTDIR) - test -z "$(strip $(EXAMPLES))" || \ - $(INSTALL_DIR) $(DISTDIR)/examples && \ - for file in $(EXAMPLES); do \ - $(INSTALL_DATA) examples/$$file $(DISTDIR)/examples; \ - done - test -z "$(strip $(MANUAL))" || \ - $(INSTALL_DIR) $(DISTDIR)/manual && \ - for file in $(MANUAL); do \ - $(INSTALL_DATA) manual/$$file $(DISTDIR)/manual; \ - done - tar --exclude-vcs -czpf $(DISTDIR).tar.gz $(DISTDIR) - -# make a Debian source package -dpkg-source: - debclean - make distclean dist - mv $(DISTDIR) $(ORIGDIR) - tar --exclude-vcs -czpf ../$(ORIGDIR).orig.tar.gz $(ORIGDIR) - rm -f -- $(DISTDIR).tar.gz - rm -rf -- $(DISTDIR) $(ORIGDIR) - cd .. && dpkg-source -b $(LIBRARY_NAME) - -etags: - etags *.h $(SOURCES) ../../pd/src/*.[ch] /usr/include/*.h /usr/include/*/*.h - -showsetup: - @echo "CFLAGS: $(CFLAGS)" - @echo "LDFLAGS: $(LDFLAGS)" - @echo "LIBS: $(LIBS)" - @echo "PD_INCLUDE: $(PD_INCLUDE)" - @echo "PD_PATH: $(PD_PATH)" - @echo "objectsdir: $(objectsdir)" - @echo "LIBRARY_NAME: $(LIBRARY_NAME)" - @echo "LIBRARY_VERSION: $(LIBRARY_VERSION)" - @echo "SOURCES: $(SOURCES)" - @echo "PDOBJECTS: $(PDOBJECTS)" - @echo "ALLSOURCES: $(ALLSOURCES)" - @echo "UNAME: $(UNAME)" - @echo "CPU: $(CPU)" - @echo "pkglibdir: $(pkglibdir)" - @echo "DISTDIR: $(DISTDIR)" - @echo "ORIGDIR: $(ORIGDIR)" +# This Makefile is based on the Makefile from pd-lib-builder written by +# Katja Vetter. You can get it from: +# https://github.com/pure-data/pd-lib-builder +PDLIBBUILDER_DIR=. +include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder diff --git a/externals/loaders/libdir/Makefile.pdlibbuilder b/externals/loaders/libdir/Makefile.pdlibbuilder new file mode 100644 index 0000000000000000000000000000000000000000..fe9a076cbe61109878e79f8c0445c2e7a6730643 --- /dev/null +++ b/externals/loaders/libdir/Makefile.pdlibbuilder @@ -0,0 +1,1215 @@ +# Makefile.pdlibbuilder dated 2016-11-02 +version = 0.4.3 + +# Helper makefile for Pure Data external libraries. +# Written by Katja Vetter March-June 2015 for the public domain. No warranties. +# Inspired by Hans Christoph Steiner's Makefile Template and Stephan Beal's +# ShakeNMake. +# +# Grab the newest version of Makefile.pdlibbuilder from +# https://github.com/pure-data/pd-lib-builder/ +# +# GNU make version >= 3.81 required. +# +# +#=== characteristics =========================================================== +# +# +# - defines build settings based on autodetected OS and architecture +# - defines rules to build Pd class- or lib executables from C or C++ sources +# - defines rules for libdir installation +# - defines convenience targets for developer and user +# - evaluates implicit dependencies for non-clean builds +# +# +#=== basic usage =============================================================== +# +# +# In your Makefile, define your Pd lib name and class files, and include +# Makefile.pdlibbuilder at the end of the Makefile. Like so: +# +# ________________________________________________________________________ +# +# # Makefile for mylib +# +# lib.name = mylib +# +# class.sources = myclass1.c myclass2.c +# +# datafiles = myclass1-help.pd myclass2-help.pd README.txt LICENSE.txt +# +# include Makefile.pdlibbuilder +# ________________________________________________________________________ +# +# +# For files in class.sources it is assumed that class basename == source file +# basename. The default target builds all classes as individual executables +# with Pd's default extension for the platform. For anything more than the +# most basic usage, continue reading. +# +# +#=== list of Makefile.pdlibbuilder API variables =============================== +# +# +# Variables available for definition in your library Makefile: +# +# - lib.name +# - lib.setup.sources +# - class.sources +# - common.sources +# - shared.sources +# - <classname>.class.sources +# - <classname>.class.ldflags +# - <classname>.class.ldlibs +# - cflags +# - ldflags +# - ldlibs +# - datafiles +# - datadirs +# - makefiles +# - makefiledirs +# - externalsdir +# +# Optional multiline defines evaluated per operating system: +# +# - forLinux +# - forDarwin +# - forWindows +# +# Variables available for your makefile or make command line: +# +# - make-lib-executable +# - suppress-wunused +# +# Path variables for make command line or environment: +# +# - PDDIR +# - PDINCLUDEDIR +# - PDBINDIR +# - PDLIBDIR +# +# Standard make variables for make command line or environment: +# +# - CPPFLAGS +# - CFLAGS +# - LDFLAGS +# - CC +# - CXX +# - INSTALL +# - DESTDIR +# +# Deprecated path variables: +# +# - PD_PATH +# - pdincludepath +# - pdbinpath +# - objectsdir +# +# +#=== descriptions of Makefile.pdlibbuilder API variables ======================= +# +# +# lib.name: +# Name of the library directory as it will be installed / distributed. Also the +# name of the lib executable in the case where all classes are linked into +# a single binary. +# +# lib.setup.sources: +# Source file(s) (C or C++) which must be compiled only when linking all classes +# into a single lib binary. +# +# class.sources: +# All sources files (C or C++) for which the condition holds that +# class name == source file basename. +# +# <classname>.class.sources: +# Source file(s) (C or C++) specific to class <classname>. Use this for +# multiple-source classes or when class name != source file basename. +# +# common.sources: +# Source file(s) which must be statically linked to each class in the library. +# +# shared.sources: +# Source file(s) (C or C++) to build a shared dynamic link lib, to be linked +# with all class executables. +# +# cflags, ldflags, ldlibs: +# Define cflags (preprocessor&compiler), ldflags (linker) and ldlibs (dynamic +# link libs) for the whole library. These flags are added to platform-specific +# flags defined by Makefile.pdlibbuilder. +# +# <classname>.class.ldflags and <classname>.class.ldlibs: +# Define ldflags resp. ldlibs specific to class <classname>. These flags are +# added to platform-specific flags defined by Makefile.pdlibbuilder, and flags +# defined in your Makefile for the whole library. Note: cflags can not be +# defined per class in the current implementation. +# +# datafiles and datadirs: +# All extra files you want to include in binary distributions of the +# library: abstractions and help patches, example patches, meta patch, readme +# and license texts, manuals, sound files, etcetera. Use 'datafiles' for all +# files that should go into your lib rootdir and 'datadirs' for complete +# directories you want to copy from source to distribution. +# +# forLinux, forDarwin, forWindows: +# Shorthand for 'variable definitions for Linux only' etc. Use like: +# define forLinux +# cflags += -DLINUX +# class.sources += linuxthing.c +# endef +# +# makefiles and makefiledirs: +# Extra makefiles or directories with makefiles that should be made in sub-make +# processes. +# +# make-lib-executable: +# When this variable is defined 'yes' in your makefile or as command argument, +# Makefile.pdlibbuilder will try to build all classes into a single library +# executable (but it will force exit if lib.setup.sources is undefined). +# If your makefile defines 'make-lib-executable=yes' as the library default, +# this can still be overriden with 'make-lib-executable=no' as command argument +# to build individual class executables (the Makefile.pdlibbuilder default.) +# +# suppress-wunused: +# When this variable is defined ('yes' or any other value), -Wunused-variable, +# -Wunused-parameter, -Wunused-value and -Wunused-function are suppressed, +# but the other warnings from -Wall are retained. +# +# PDDIR: +# Root directory of 'portable' pd package. When defined, PDINCLUDEDIR and +# PDBINDIR will be evaluated as $(PDDIR)/src and $(PDDIR)/bin. +# +# PDINCLUDEDIR: +# Directory where Pd API m_pd.h should be found, and other Pd header files. +# Overrides the default search path. +# +# PDBINDIR: +# Directory where pd.dll should be found for linking (Windows only). Overrides +# the default search path. +# +# PDLIBDIR: +# Root directory for installation of Pd library directories. Overrides the +# default install location. +# +# DESTDIR: +# Prepended path component for staged install. +# +# CPPFLAGS: +# Preprocessor flags which are not strictly required for building. +# +# CFLAGS: +# Compiler flags which are not strictly required for building. Compiler flags +# defined by Makefile.pdlibbuilder for warning, optimization and architecture +# specification are overriden by CFLAGS. +# +# LDFLAGS: +# Linker flags which are not strictly required for building. Linker flags +# defined by Makefile.pdlibbuilder for architecture specification are overriden +# by LDFLAGS. +# +# CC and CXX: +# C and C++ compiler programs as defined in your build environment. +# +# INSTALL +# Definition of install program. +# +# PD_PATH: +# Equivalent to PDDIR. Supported for compatibility with pd-extended central +# makefile, but deprecated otherwise. +# +# objectsdir: +# Root directory for installation of Pd library directories, like PDLIBDIR but +# not overridable by environment. Supported for compatibility with pd-extended +# central makefile, but deprecated otherwise. +# +# pdincludepath, pdbinpath: +# As PDINCLUDEDIR and PDBINDIR but not overridable by environment. Deprecated +# as user variables. +# +# +#=== paths ===================================================================== +# +# +# Source files in directories other than current working directory must be +# prefixed with their relative path. Do not rely on VPATH or vpath. +# Object (.o) files are built in the directory of their source files. +# Executables are built in current working directory. +# +# Default search path for m_pd.h and other API header files is platform +# dependent, and overridable by PDINCLUDEDIR: +# +# Linux: /usr/include/pd +# +# OSX: /Applications/Pd*.app/Contents/Resources/src +# +# Windows: %PROGRAMFILES%/pd/src +# +# Default location to install pd libraries is platform dependent, and +# overridable by PDLIBDIR: +# +# Linux: /usr/local/lib/pd-externals +# OSX: ~/Library/Pd +# Windows: %APPDATA%/Pd +# +# https://puredata.info/docs/faq/how-do-i-install-externals-and-help-files +# The rationale for not installing to ~/pd-externals by default on Linux +# is that some people share the home dir between 32 and 64 bit installations. +# +# +#=== targets =================================================================== +# +# +# all: build $(executables) plus optional post target +# post: target to build after $(executables) +# alldebug: build all with -g option turned on for debug symbols +# <classname>: force clean build of an individual class +# <sourcefile>.pre: make preprocessor output file in current working directory +# <sourcefile>.lst: make asm/source output file in current working directory +# +# install: install executables and data files +# clean: remove build products from source tree +# +# help: print help text +# vars: print makefile variables +# allvars: print all variables +# depend: print generated prerequisites +# coffee: dummy target +# +# Variable $(executables) expands to class executables plus optional shared lib, +# or alternatively to single lib executable when make-lib-executable=true. +# Targets pre and post can be defined by library makefile. Make sure to include +# Makefile.pdlibbuilder first so default target all will not be redefined. +# +# +#=== Pd-extended libdir concept ================================================ +# +# +# For libdir layout as conceived by Hans-Christoph Steiner, see: +# +# https://puredata.info/docs/developer/Libdir +# +# Files README.txt, LICENSE.txt and <lib.name>-meta.pd are part of the libdir +# convention. Help patches for each class and abstraction are supposed to be +# available. Makefile.pdlibbuilder does not force the presence of these files +# however. It does not automatically include such files in libdir installations. +# Data files you want to include in distributions must be defined explicitly in +# your Makefile. +# +# +#=== Makefile.pdlibbuilder syntax conventions ================================== +# +# +# Makefile.pdlibbuilder variable names are lower case. Default make variables, +# environment variables, and standard user variables (CC, CXX, CFLAGS, DESTDIR) +# are upper case. Use target 'allvars' to print all variables and their values. +# +# 'Fields' in data variables are separated by dots, like in 'foo.class.sources'. +# Words in variables expressing a function or command are separated by dashes, +# like in 'make-lib-executable'. +# +# +#=== useful make options ======================================================= +# +# +# Use 'make -d <target>' to print debug details of the make process. +# Use 'make -p <target>' to print make's database. +# +# +#=== TODO ====================================================================== +# +# +# - decide whether to use -static-libgcc or shared dll in MinGW +# - cygwin support +# - android support +# - Windows 64 bit support +# - figure out how to handle '$' in filenames +# - add makefile template targets dpkg-source dist libdir distclean tags? +# +# +#=== end of documentation sections ============================================= +# +# +################################################################################ +################################################################################ +################################################################################ + + +# GNU make version 3.81 (2006) or higher is required because of the following: +# - function 'info' +# - variable '.DEFAULT_GOAL' + +# force exit when make version is < 3.81 +ifneq ($(firstword $(sort 3.81 $(MAKE_VERSION))), 3.81) + $(error GNU make version 3.81 or higher is required) +endif + +# Relative path to externals root dir in multi-lib source tree like +# pd-extended SVN. Default is parent of current working directory. May be +# defined differently in including makefile. +externalsdir ?= .. + +# variable you can use to check if Makefile.pdlibbuilder is already included +Makefile.pdlibbuilder = true + + +################################################################################ +### variables: library name and version ######################################## +################################################################################ + + +# strip possibles spaces from lib.name, they mess up calculated file names +lib.name := $(strip $(lib.name)) + +# if meta file exists, check library version +metafile := $(wildcard $(lib.name)-meta.pd) + +ifdef metafile + lib.version := $(shell sed -n \ + 's|^\#X text [0-9][0-9]* [0-9][0-9]* VERSION \(.*\);|\1|p' \ + $(metafile)) +endif + + +################################################################################ +### variables: files ########################################################### +################################################################################ + + +#=== sources =================================================================== + + +# (re)define <classname>.class.sources using file names in class.sources + +define add-class-source +$(notdir $(basename $v)).class.sources += $v +endef + +$(foreach v, $(class.sources), $(eval $(add-class-source))) + +# derive class names from <classname>.class.sources variables +sourcevariables := $(filter %.class.sources, $(.VARIABLES)) +classes := $(basename $(basename $(sourcevariables))) + +# accumulate all source files specified in makefile +classes.sources := $(sort $(foreach v, $(sourcevariables), $($v))) +all.sources := $(classes.sources) $(lib.setup.sources) \ + $(shared.sources) $(common.sources) + + +#=== object files ============================================================== + + +# construct object filenames from all C and C++ source file names +classes.objects := $(addsuffix .o, $(basename $(classes.sources))) +common.objects := $(addsuffix .o, $(basename $(common.sources))) +shared.objects := $(addsuffix .o, $(basename $(shared.sources))) +lib.setup.objects := $(addsuffix .o, $(basename $(lib.setup.sources))) +all.objects = $(classes.objects) $(common.objects) $(shared.objects) \ + $(lib.setup.objects) + + +#=== executables =============================================================== + + +# use recursive variables here because executable extension is not yet known + +# construct class executable names from class names +classes.executables = $(addsuffix .$(extension), $(classes)) + +# construct shared lib executable name if shared sources are defined +ifdef shared.sources + shared.lib = lib$(lib.name).$(shared.extension) +else + shared.lib = +endif + + +################################################################################ +### variables per platform ##################################################### +################################################################################ + + +#=== flags per architecture ==================================================== + + +# Set architecture-dependent cflags, mainly for Linux. For Mac and Windows, +# arch.c.flags are overriden below. + +machine := $(shell uname -m) + +# Raspberry Pi 1st generation +ifeq ($(machine), armv6l) + arch.c.flags = -march=armv6 -mfpu=vfp -mfloat-abi=hard +endif + +# Beagle, Udoo, RPi2 etc. +ifeq ($(machine), armv7l) + arch.c.flags = -march=armv7-a -mfpu=vfpv3 -mfloat-abi=hard +endif + +# Intel 32 bit, build with SSE and SSE2 instructions +ifeq ($(findstring $(machine), i386 i686), $(machine)) + arch.c.flags = -march=pentium4 -mfpmath=sse -msse -msse2 +endif + +# Intel/AMD 64 bit, build with SSE, SSE2 and SSE3 instructions +ifeq ($(findstring $(machine), ia64 x86_64), $(machine)) + arch.c.flags = -march=core2 -mfpmath=sse -msse -msse2 -msse3 +endif + + +#=== operating system ========================================================== + + +# The following systems are defined: Linux, Darwin, Windows. GNU and +# GNU/kFreeBSD are treated as Linux to get the same options. System-specific +# multiline defines (optionally set in library makefile) are conditionally +# evaluated here. + +uname := $(shell uname) + +ifeq ($(findstring $(uname), Linux GNU GNU/kFreeBSD), $(uname)) + system = Linux + $(eval $(forLinux)) +endif + +ifeq ($(uname), Darwin) + system = Darwin + $(eval $(forDarwin)) +endif + +ifeq ($(findstring MINGW, $(uname)), MINGW) + system = Windows + $(eval $(forWindows)) +endif + +# TODO: Cygwin, Android + + +#=== flags and paths for Linux ================================================= + + +ifeq ($(system), Linux) + prefix = /usr/local + libdir := $(prefix)/lib + pkglibdir = $(libdir)/pd-externals + pdincludepath := $(wildcard /usr/include/pd) + extension = pd_linux + cpp.flags := -DUNIX + c.flags := -fPIC + c.ldflags := -rdynamic -shared -fPIC -Wl,-rpath,"\$$ORIGIN",--enable-new-dtags + c.ldlibs := -lc -lm + cxx.flags := -fPIC -fcheck-new + cxx.ldflags := -rdynamic -shared -fPIC -Wl,-rpath,"\$$ORIGIN",--enable-new-dtags + cxx.ldlibs := -lc -lm -lstdc++ + shared.extension = so + shared.ldflags := -rdynamic -fPIC -shared -Wl,-soname,$(shared.lib) + stripflags = --strip-unneeded -R .note -R .comment +endif + + +#=== flags and paths for Darwin ================================================ + + +# On OSX we try to build fat binaries by default. It is assumed that OSX i386 +# can build for ppc and OSX x86_64 can't. TODO: try to refine this condition. +# LLVM-clang doesn't support -fcheck-new, therefore this flag is omitted for +# OSX x86_64. + + +ifeq ($(system), Darwin) + pkglibdir = $(HOME)/Library/Pd + pdincludepath := $(firstword $(wildcard \ + /Applications/Pd*.app/Contents/Resources/src)) + extension = pd_darwin + cpp.flags := -DUNIX -DMACOSX -I /sw/include + c.flags := + c.ldflags := -undefined suppress -flat_namespace -bundle + c.ldlibs := -lc + cxx.ldflags := -undefined suppress -flat_namespace -bundle + cxx.ldlibs := -lc + shared.extension = dylib + shared.ldflags = -dynamiclib -undefined dynamic_lookup \ + -install_name @loader_path/$(shared.lib) \ + -compatibility_version 1 -current_version 1.0 + stripflags = -x + version.flag := $(filter $(cflags), -mmacosx-version-min=%) + ifeq ($(machine), i386) + cxx.flags := -fcheck-new + arch := ppc i386 x86_64 + version.flag ?= -mmacosx-version-min=10.4 + endif + ifeq ($(machine), x86_64) + arch := i386 x86_64 + version.flag ?= -mmacosx-version-min=10.5 + endif + arch.c.flags := $(addprefix -arch , $(arch)) $(version.flag) + arch.ld.flags := $(arch.c.flags) +endif + + +#=== flags and paths for Windows =============================================== + + +# Standard paths on Windows contain spaces, and GNU make functions treat such +# paths as lists, with unintended effects. Therefore we must use shell function +# ls instead of make's wildcard, and probe for each standard path individually. +# Using double quotes around paths with spaces is obligatory. Since some path +# variables are assembled or re-expanded later, great care must be taken to put +# quotes at appropriate points throughout the makefile. Thanks, Bill. + +# paths for 32-bit executables on 64-bit Windows aren't yet defined here (TODO) +ifeq ($(system), Windows) + pkglibdir := $(APPDATA)/Pd + ifndef pdbinpath + pdbinpath := $(shell ls -d "$(PROGRAMFILES)/pd/bin") + endif + ifndef pdincludepath + pdincludepath := $(shell ls -d "$(PROGRAMFILES)/pd/src") + endif +endif + +# On Windows we build 32 bit by default to match Pd(-extended) binary +# distributions. This may change in the future. +# TODO: decide whether -mms-bitfields should be specified. +ifeq ($(system), Windows) + extension = dll + CC = gcc + CXX = g++ + arch.c.flags := -march=pentium4 -msse -msse2 -mfpmath=sse + cpp.flags := -DMSW -DNT + c.flags := + c.ldflags = -static-libgcc -shared \ + -Wl,--enable-auto-import "$(pdbinpath)/pd.dll" + c.ldlibs := + cxx.flags := -fcheck-new + cxx.ldflags = -static-libstdc++ -shared \ + -Wl,--enable-auto-import "$(pdbinpath)/pd.dll" + cxx.ldlibs := + shared.extension = dll + shared.ldflags = -static-libgcc -shared "$(pdbinpath)/pd.dll" + stripflags = --strip-unneeded -R .note -R .comment +endif + + +#=== paths ===================================================================== + + +# Platform-dependent default paths are specified above, but overridable. +# Path variables in upper case can be defined as make command argument or in the +# environment. 'PD_PATH' and 'objectsdir' are supported for compatibility with +# the build system that pd-l2ork has inherited from pd-extended. + +PDDIR ?= $(PD_PATH) +PDINCLUDEDIR ?= $(pdincludepath) +PDBINDIR ?= $(pdbinpath) +PDLIBDIR ?= $(firstword $(objectsdir) $(pkglibdir)) + +ifneq ($(PDDIR),) + PDINCLUDEDIR := $(wildcard $(PDDIR)/src) + PDBINDIR := $(wildcard $(PDDIR)/bin) +endif + +# base path where all components of the lib will be installed by default +installpath := $(DESTDIR)$(PDLIBDIR)/$(lib.name) + +# check if include path contains spaces (as is often the case on Windows) +# if so, store the path so we can later do checks with it +pdincludepathwithspaces := $(if $(word 2, $(PDINCLUDEDIR)), $(PDINCLUDEDIR)) + + +#=== accumulated build flags =================================================== + + +# From GNU make docs: 'Users expect to be able to specify CFLAGS freely +# themselves.' So we use CFLAGS to define options which are not strictly +# required for compilation: optimizations, architecture specifications, and +# warnings. CFLAGS can be safely overriden using a make command argument. +# Variables cflags, ldflags and ldlibs may be defined in including makefile. + +optimization.flags = -O3 -ffast-math -funroll-loops -fomit-frame-pointer +warn.flags = -Wall -Wextra -Wshadow -Winline -Wstrict-aliasing + +# suppress -Wunused-variable & Co if you don't want to clutter a build log +ifdef suppress-wunused + warn.flags += $(addprefix -Wno-unused-, function parameter value variable) +endif + +CFLAGS = $(warn.flags) $(optimization.flags) $(arch.c.flags) + +# preprocessor flags +cpp.flags := -DPD -I "$(PDINCLUDEDIR)" $(cpp.flags) $(CPPFLAGS) + +# flags for dependency checking (cflags from makefile may define -I options) +depcheck.flags := $(cpp.flags) $(cflags) + +# architecture specifications for linker are overridable by LDFLAGS +LDFLAGS := $(arch.ld.flags) + +# now add the same ld flags to shared dynamic lib +shared.ldflags := $(shared.ldflags) $(LDFLAGS) + +# accumulated flags for C compiler / linker +c.flags := $(cpp.flags) $(c.flags) $(cflags) $(CFLAGS) +c.ldflags := $(c.ldflags) $(ldflags) $(LDFLAGS) +c.ldlibs := $(c.ldlibs) $(ldlibs) + +# accumulated flags for C++ compiler / linker +cxx.flags := $(cpp.flags) $(cxx.flags) $(cflags) $(CFLAGS) +cxx.ldflags := $(cxx.ldflags) $(ldflags) $(LDFLAGS) +cxx.ldlibs := $(cxx.ldlibs) $(ldlibs) + + +################################################################################ +### variables: tools ########################################################### +################################################################################ + + +# aliases so we can later define 'compile-$1' and set 'c' or 'cxx' as argument +compile-c := $(CC) +compile-cxx := $(CXX) + + +################################################################################ +### checks ##################################################################### +################################################################################ + + +# At this point most variables are defined. Now do some checks and info's +# before rules begin. + +# 'forward declaration' of default target, needed to do checks +all: + +# To avoid unpredictable results, make sure the default target is not redefined +# by including makefile. +ifneq ($(.DEFAULT_GOAL), all) + $(error Default target must be 'all'.) +endif + +# find out which target(s) will be made +ifdef MAKECMDGOALS + goals := $(MAKECMDGOALS) +else + goals := all +endif + +# store path to Pd API m_pd.h if it is found +ifdef PDINCLUDEDIR + mpdh := $(shell ls "$(PDINCLUDEDIR)/m_pd.h") +endif + +# print Makefile.pdlibbuilder version +$(info ++++ info: using Makefile.pdlibbuilder version $(version)) + +# when making target all, check if m_pd.h is found and print info about it +ifeq ($(goals), all) + $(if $(mpdh), \ + $(info ++++ info: using Pd API $(mpdh)), \ + $(warning Where is Pd API m_pd.h? Do 'make help' for info.)) +endif + +# print target info +$(info ++++ info: making target $(goals) $(if $(lib.name),in lib $(lib.name))) + +# when installing, print installpath info +$(if $(filter install install-lib, $(goals)), $(info ++++ info: \ + installpath is '$(installpath)')) + + +#=== define executables ======================================================== + + +# By default we build class executables, and optionally a shared dynamic link +# lib. When make-lib-executable=yes we build all classes into a single lib +# executable, on the condition that variable lib.setup.sources is defined. + +ifeq ($(make-lib-executable),yes) + $(if $(lib.setup.sources), ,\ + $(error Can not build library blob because lib.setup.sources is undefined)) + executables := $(lib.name).$(extension) +else + executables := $(classes.executables) $(shared.lib) +endif + + +################################################################################ +### rules: special targets ##################################################### +################################################################################ + + +# Disable built-in rules. If some target can't be built with the specified +# rules, it should not be built at all. +MAKEFLAGS += --no-builtin-rules + +.PRECIOUS: +.SUFFIXES: +.PHONY: all post build-lib \ + $(classes) $(makefiledirs) $(makefiles) \ + install install-executables install-datafiles install-datadirs \ + force clean vars allvars depend help + + +################################################################################ +### rules: build targets ####################################################### +################################################################################ + + +# Target all forces the build of targets [$(executables) post] in +# deterministic order. Target $(executables) builds class executables plus +# optional shared lib or alternatively a single lib executable when +# make-lib-executable=true. Target post is optionally defined by +# library makefile. + +all: post +post: $(executables) + +all: + $(info ++++info: target all in lib $(lib.name) completed) + +# build all with -g option turned on for debug symbols +alldebug: c.flags += -g +alldebug: cxx.flags += -g +alldebug: all + + +#=== class executable ========================================================== + + +# recipe for linking objects in class executable +# argument $1 = compiler type (c or cxx) +# argument $2 = class basename +define link-class + $(compile-$1) \ + $($1.ldflags) $($2.class.ldflags) \ + -o $2.$(extension) \ + $(addsuffix .o, $(basename $($2.class.sources))) \ + $(addsuffix .o, $(basename $(common.sources))) \ + $($1.ldlibs) $($2.class.ldlibs) $(shared.lib) +endef + +# general rule for linking object files in class executable +%.$(extension): $(shared.lib) + $(info ++++ info: linking objects in $@ for lib $(lib.name)) + $(if $(filter %.cc %.cpp, $($*.class.sources)), \ + $(call link-class,cxx,$*), \ + $(call link-class,c,$*)) + + +#=== library blob ============================================================== + + +# build all classes into single executable +build-lib: $(lib.name).$(extension) + $(info ++++ info: library blob $(lib.name).$(extension) completed) + +# recipe for linking objects in lib executable +# argument $1 = compiler type (c or cxx) +define link-lib + $(compile-$1) \ + $($1.ldflags) $(lib.ldflags) \ + -o $(lib.name).$(extension) $(all.objects) \ + $($1.ldlibs) $(lib.ldlibs) +endef + +# rule for linking objects in lib executable +# declared conditionally to avoid name clashes +ifeq ($(make-lib-executable),yes) +$(lib.name).$(extension): $(all.objects) + $(if $(filter %.cc %.cpp, $(all.sources)), \ + $(call link-lib,cxx), \ + $(call link-lib,c)) +endif + + +#=== shared dynamic lib ======================================================== + + +# recipe for linking objects in shared executable +# argument $1 = compiler type (c or cxx) +define link-shared + $(compile-$1) \ + $(shared.ldflags) \ + -o lib$(lib.name).$(shared.extension) $(shared.objects) \ + $($1.ldlibs) $(shared.ldlibs) +endef + +# rule for linking objects in shared executable +# build recipe is in macro 'link-shared' +lib$(lib.name).$(shared.extension): $(shared.objects) + $(info ++++ info: linking objects in shared lib $@) + $(if $(filter %.cc %.cpp, $(shared.sources)), \ + $(call link-shared,cxx), \ + $(call link-shared,c)) + + +#=== object files ============================================================== + + +# recipe to make .o file from source +# argument $1 is compiler type (c or cxx) +define make-object-file + $(info ++++ info: making $@ in lib $(lib.name)) + $(compile-$1) \ + $($1.flags) \ + -o $@ -c $< +endef + +# Three rules to create .o files. These are double colon 'terminal' rules, +# meaning they are the last in a rules chain. + +%.o:: %.c + $(call make-object-file,c) + +%.o:: %.cc + $(call make-object-file,cxx) + +%.o:: %.cpp + $(call make-object-file,cxx) + + +#=== explicit prerequisites for class executables ============================== + + +# For class executables, prerequisite rules are declared in run time. Target +# 'depend' prints these rules for debugging purposes. + +# declare explicit prerequisites rule like 'class: class.extension' +# argument $v is class basename +define declare-class-target +$v: $v.$(extension) +endef + +# declare explicit prerequisites rule like 'class.extension: object1.o object2.o' +# argument $v is class basename +define declare-class-executable-target +$v.$(extension): $(addsuffix .o, $(basename $($v.class.sources))) \ + $(addsuffix .o, $(basename $(common.sources))) +endef + +# evaluate explicit prerequisite rules for all classes +$(foreach v, $(classes), $(eval $(declare-class-target))) +$(foreach v, $(classes), $(eval $(declare-class-executable-target))) + + +#=== implicit prerequisites for class executables ============================== + + +# Evaluating implicit prerequisites (header files) with help from the +# preprocessor is 'expensive' so this is done conditionally and selectively. +# Note that it is also possible to trigger a build via install targets, in +# which case implicit prerequisites are not checked. + +# When the Pd include path contains spaces it will mess up the implicit +# prerequisites rules. +disable-dependency-tracking := $(strip $(pdincludepathwithspaces)) + +ifndef disable-dependency-tracking + must-build-everything := $(filter all, $(goals)) + must-build-class := $(filter $(classes), $(goals)) + must-build-sources := $(foreach v, $(must-build-class), $($v.class.sources)) +endif + +# declare implicit prerequisites rule like 'object.o: header1.h header2.h ...' +# argument $1 is input source file(s) +# dir is explicitly added because option -MM strips it by default +define declare-object-target +$(dir $1)$(filter %.o: %.h, $(shell $(CPP) $(depcheck.flags) -MM $1)) $(MAKEFILE_LIST) +endef + +# evaluate implicit prerequisite rules when rebuilding everything +ifdef must-build-everything + $(if $(wildcard $(all.objects)), \ + $(info ++++ info: evaluating implicit prerequisites in lib $(lib.name).....) \ + $(foreach v, $(all.sources), $(eval $(call declare-object-target, $v)))) +endif + +# evaluate implicit prerequisite rules when selectively building classes +ifdef must-build-class + $(foreach v, $(must-build-sources), \ + $(eval $(call declare-object-target, $v))) + $(foreach v, $(shared.sources), \ + $(eval $(call declare-object-target, $v))) +endif + + +################################################################################ +### rules: preprocessor and assembly files ##################################### +################################################################################ + + +# Preprocessor and assembly output files for bug tracing etc. They are not part +# of the build processes for executables. By default these files are created in +# the current working directory. Dependency tracking is not performed, the build +# is forced instead to make sure it's up to date. + +force: + + +#=== preprocessor file ========================================================= + + +# make preprocessor output file with extension .pre +# argument $1 = compiler type (c or cxx) +define make-preprocessor-file + $(info ++++ info: making preprocessor output file $(notdir $*.pre) \ + in current working directory) + $(compile-$1) -E $< $(c.flags) $($1.flags) -o $(notdir $*.pre) +endef + +%.pre:: %.c force + $(call make-preprocessor-file,c) + +%.pre:: %.cc force + $(call make-preprocessor-file,cxx) + +%.pre:: %.cpp force + $(call make-preprocessor-file,cxx) + + +#=== assembly file ============================================================= + + +# make C / assembly interleaved output file with extension .lst +# argument $1 = compiler type (c or cxx) +define make-assembly-file + $(info ++++ info: making assembly output file $(notdir $*.lst) \ + in current working directory) + $(compile-$1) \ + -c -Wa,-a,-ad -fverbose-asm \ + $($1.flags) \ + $< > $(notdir $*.lst) +endef + +%.lst:: %.c force + $(call make-assembly-file,c) + +%.lst:: %.cc force + $(call make-assembly-file,cxx) + +%.lst:: %.cpp force + $(call make-assembly-file,cxx) + + +################################################################################ +### rules: installation targets ################################################ +################################################################################ + + +# Install targets depend on successful exit status of target all because nothing +# must be installed in case of a build error. + + +# -p = preserve time stamps +# -m = set permission mode (as in chmod) +# -d = create all components of specified directories +INSTALL = install +INSTALL_PROGRAM := $(INSTALL) -p -m 644 +INSTALL_DATA := $(INSTALL) -p -m 644 +INSTALL_DIR := $(INSTALL) -m 755 -d + +# strip spaces from file names +executables := $(strip $(executables)) +datafiles := $(strip $(datafiles)) +datadirs := $(strip $(datadirs)) + +# Do not make any install sub-target with empty variable definition because the +# install program would exit with an error. +install: $(if $(executables), install-executables) +install: $(if $(datafiles), install-datafiles) +install: $(if $(datadirs), install-datadirs) + +install-executables: all + $(INSTALL_DIR) -v "$(installpath)" + $(foreach v, $(executables), \ + $(INSTALL_PROGRAM) '$v' "$(installpath)";) + $(info ++++ info: executables of lib $(lib.name) installed \ + from $(CURDIR) to $(installpath)) + +install-datafiles: all + $(INSTALL_DIR) -v "$(installpath)" + $(foreach v, $(datafiles), \ + $(INSTALL_DATA) '$(v)' "$(installpath)";) + $(info ++++ info: data files of lib $(lib.name) installed \ + from $(CURDIR) to $(installpath)) + +install-datadirs: all + $(foreach v, $(datadirs), $(INSTALL_DIR) "$(installpath)/$v";) + $(foreach v, $(datadirs), \ + $(INSTALL_DATA) $(wildcard $v/*) "$(installpath)/$v";) + $(info ++++ info: data directories of lib $(lib.name) installed \ + from $(CURDIR) to $(installpath)) + + +################################################################################ +### rules: distribution targets ################################################ +################################################################################ + + +# TODO +# These targets are implemented in Makefile Template, but I have to figure out +# how to do it under the not-so-strict conditions of Makefile.pdlibbuilder. + +# make source package +dist: + @echo "target dist not yet implemented" + +# make Debian source package +dpkg-source: + @echo "target dpkg-source not yet implemented" + +$(ORIGDIR): + +$(DISTDIR): + + +################################################################################ +### rules: clean targets ####################################################### +################################################################################ + + +# delete build products from build tree +clean: + rm -f $(all.objects) + rm -f $(classes.executables) $(lib.name).$(extension) $(shared.lib) + rm -f *.pre *.lst + +# remove distribution directories and tarballs from build tree +distclean: clean + @echo "target distclean not yet implemented" + + +################################################################################ +### rules: submake targets ##################################################### +################################################################################ + + +# Iterate over sub-makefiles or makefiles in other directories. + +# When 'continue-make=yes' is set, sub-makes will report 'true' to the parent +# process regardless of their real exit status. This prevents the parent make +# from being aborted by a sub-make error. Useful when you want to quickly find +# out which sub-makes from a large set will succeed. +ifeq ($(continue-make),yes) + continue = || true +endif + +# These targets will trigger sub-make processes for entries in 'makefiledirs' +# and 'makefiles'. +all alldebug install clean distclean dist dkpg-source: \ + $(makefiledirs) $(makefiles) + +# this expands to identical rules for each entry in 'makefiledirs' +$(makefiledirs): + $(MAKE) --directory=$@ $(MAKECMDGOALS) $(continue) + +# this expands to identical rules for each entry in 'makefiles' +$(makefiles): + $(MAKE) --directory=$(dir $@) --makefile=$(notdir $@) $(MAKECMDGOALS) $(continue) + + +################################################################################ +### rules: convenience targets ################################################# +################################################################################ + + +#=== show variables ============================================================ + + +# Several 'function' macro's cause errors when expanded within a rule or without +# proper arguments. Variables which are set with the define directive are only +# shown by name for that reason. +functions = \ +add-class-source \ +declare-class-target \ +declare-class-executable-target \ +declare-object-target \ +link-class \ +link-lib \ +link-shared \ +make-object-file \ +make-preprocessor-file \ +make-assembly-file + + +# show variables from makefiles +vars: + $(info ++++ info: showing makefile variables:) + $(foreach v,\ + $(sort $(filter-out $(functions) functions, $(.VARIABLES))),\ + $(if $(filter file, $(origin $v)),\ + $(info variable $v = $($v)))) + $(foreach v, $(functions), $(info 'function' name: $v)) + @echo + +# show all variables +allvars: + $(info ++++ info: showing default, automatic and makefile variables:) + $(foreach v, \ + $(sort $(filter-out $(functions) functions, $(.VARIABLES))), \ + $(info variable ($(origin $v)) $v = $($v))) + $(foreach v, $(functions), $(info 'function' name: $v)) + @echo + + +#=== show dependencies ========================================================= + + +# show generated prerequisites rules +depend: + $(info ++++ info: generated prerequisite rules) + $(foreach v, $(classes), $(info $(declare-class-target))) + $(foreach v, $(classes), $(info $(declare-class-executable-target))) + $(foreach v, $(all.sources), $(info $(call declare-object-target, $v))) + @echo + + +#=== show help text ============================================================ + + +# brief info about targets and paths + +ifdef mpdh + mpdhinfo := $(mpdh) +else + mpdhinfo := m_pd.h was not found. Is Pd installed? +endif + +help: + @echo + @echo " Main targets:" + @echo " all: build executables (default target)" + @echo " install: install all components of the library" + @echo " vars: print makefile variables for troubleshooting" + @echo " allvars: print all variables for troubleshooting" + @echo " help: print this help text" + @echo + @echo " Pd API m_pd.h:" + @echo " $(mpdhinfo)" + @echo " You may specify your preferred Pd include directory as argument" + @echo " to the make command, like 'PDINCLUDEDIR=path/to/pd/src'." + @echo + @echo " Path for installation of your libdir(s):" + @echo " $(PDLIBDIR)" + @echo " Alternatively you may specify your path for installation as argument" + @echo " to the make command, like 'PDLIBDIR=path/to/pd-externals'." + @echo + @echo " Default paths are listed in the doc sections in Makefile.pdlibbuilder." + @echo + + +#=== dummy target ============================================================== + + +coffee: + @echo "Makefile.pdlibbuilder: Can not make coffee. Sorry." + + +################################################################################ +### end of rules sections ###################################################### +################################################################################ + + +# for syntax highlighting in vim and github +# vim: set filetype=make: + diff --git a/externals/loaders/libdir/libdir-meta.pd b/externals/loaders/libdir/libdir-meta.pd index 4172d61b7f5c2c4decc32f2728e3016c8416e4da..2d991a7d71306cb9cf8ef0e7895fd296428bc4fd 100644 --- a/externals/loaders/libdir/libdir-meta.pd +++ b/externals/loaders/libdir/libdir-meta.pd @@ -2,6 +2,6 @@ #N canvas 25 49 420 300 META 1; #X text 10 10 NAME libdir; #X text 10 30 AUTHOR hans@eds.org; -#X text 10 50 VERSION 1.9; +#X text 10 50 VERSION 1.10; #X text 10 70 LICENSE BSD; #X restore 10 10 pd META; diff --git a/externals/loaders/libdir/libdir.c b/externals/loaders/libdir/libdir.c index 8609e3ceed53a4b271c6777bd3b89958e8a829bb..0588beda68335a7d6f0dd2055dae0a2a01a480e3 100644 --- a/externals/loaders/libdir/libdir.c +++ b/externals/loaders/libdir/libdir.c @@ -10,7 +10,7 @@ /* WARNING: KLUDGE! */ /* * this struct is not publically defined (its in g_canvas.c) so I need to - * include this here. Its from Pd 0.41-test03 2006-11-19. */ + * include this here. Its from Pd 0.47-0. */ struct _canvasenvironment { t_symbol *ce_dir; /* directory patch lives in */ @@ -21,7 +21,13 @@ struct _canvasenvironment }; -static char *version = "1.9"; +static char *version = +#ifdef VERSION + VERSION +#else + "unknown" +#endif + ; /* This loader opens a directory with a -meta.pd file as a library. In the * long run, the idea is that one folder will have all of objects files, all @@ -37,71 +43,131 @@ static char *version = "1.9"; * to have one directory hold the objects, help files, manuals, * etc. making it a self-contained library. <hans@at.or.at> */ +/* + * TODO + * - get 'declare' messages from the meta-file, and send them to the current canvas + */ +static void libdir_get_fullname(char*dest, size_t size, const char*classname) { + snprintf(dest, size-1, "%s/%s-meta", classname, classname); + dest[size-1]=0; +} +static int libdir_add_to_path(const char*dirbuf, t_canvas*canvas) { + if(sys_isabsolutepath(dirbuf)) { // only include actual full paths + if (canvas) { + t_canvasenvironment *canvasenvironment = canvas_getenv(canvas); + canvasenvironment->ce_path = namelist_append(canvasenvironment->ce_path, + dirbuf, 0); + } else { + sys_searchpath = namelist_append(sys_searchpath, dirbuf, 0); + } + return 1; + } + return 0; +} -static int libdir_loader(t_canvas *canvas, char *classname) +static int libdir_loader_legacy(t_canvas *canvas, char *classname) { int fd = -1; - char helppathname[FILENAME_MAX]; char fullclassname[FILENAME_MAX], dirbuf[FILENAME_MAX]; char *nameptr; - t_canvasenvironment *canvasenvironment; /* look for meta file (classname)/(classname)-meta.pd */ - strncpy(fullclassname, classname, FILENAME_MAX - 6); - strcat(fullclassname, "/"); - strncat(fullclassname, classname, FILENAME_MAX - strlen(fullclassname) - 6); - strcat(fullclassname, "-meta"); - + libdir_get_fullname(fullclassname, FILENAME_MAX, classname); + /* if this is being called from a canvas, then add the library path to the * canvas-local path */ - if(canvas) - { - canvasenvironment = canvas_getenv(canvas); - /* setting the canvas to NULL causes it to ignore any canvas-local path */ - if ((fd = canvas_open(NULL, fullclassname, ".pd", - dirbuf, &nameptr, FILENAME_MAX, 0)) < 0) - { - return (0); - } - close(fd); - if(sys_isabsolutepath(dirbuf)) // only include actual full paths - canvasenvironment->ce_path = namelist_append(canvasenvironment->ce_path, - dirbuf, 0); - if(sys_verbose) - post("libdir_loader: added '%s' to the canvas-local objectclass path", - classname); - } + if(canvas) + /* setting the canvas to NULL causes it to ignore any canvas-local path */ + fd = canvas_open(NULL, fullclassname, ".pd", + dirbuf, &nameptr, FILENAME_MAX, 0); else + fd = open_via_path(".", fullclassname, ".pd", + dirbuf, &nameptr, FILENAME_MAX, 0); + if(fd < 0) { - if ((fd = open_via_path(".", fullclassname, ".pd", - dirbuf, &nameptr, FILENAME_MAX, 0)) < 0) - { - return (0); - } - close(fd); - sys_searchpath = namelist_append(sys_searchpath, dirbuf, 0); - strncpy(helppathname, sys_libdir->s_name, FILENAME_MAX-30); - helppathname[FILENAME_MAX-30] = 0; - strcat(helppathname, "/doc/5.reference/"); - strcat(helppathname, classname); - sys_helppath = namelist_append(sys_helppath, helppathname, 0); - post("libdir_loader: added '%s' to the global objectclass path", - classname); -// post("\tThis is deprecated behavior."); + return (0); + } + sys_close(fd); +#if 0 + if(!canvas) { + char helppathname[FILENAME_MAX]; + strncpy(helppathname, sys_libdir->s_name, FILENAME_MAX-30); + helppathname[FILENAME_MAX-30] = 0; + strcat(helppathname, "/doc/5.reference/"); + strcat(helppathname, classname); + sys_helppath = namelist_append(sys_helppath, helppathname, 0); + } +#endif + if(libdir_add_to_path(dirbuf, canvas)) + logpost(NULL, 3, "libdir_loader: added '%s' to the %s objectclass path", + classname, canvas?"canvas-local":"global"); + + /* post("libdir_loader loaded fullclassname: '%s'\n", fullclassname); */ +#if 0 + // AG: This is from https://github.com/pure-data/libdir/commit/2f7b873e. + // Seems overly verbose, though, disasbled for now since Purr Data won't + // filter console messages according to their verbosity level. + logpost(NULL, 14, "Loaded libdir '%s' from '%s'", classname, dirbuf); +#endif + + return (1); +} + +static int libdir_loader_pathwise(t_canvas *canvas, const char *classname, const char*path) +{ + int fd = -1; + char fullclassname[FILENAME_MAX], dirbuf[FILENAME_MAX]; + char *nameptr; + + if(!path) { + /* we already tried all paths, so skip this */ + return 0; + } + + /* look for meta file (classname)/(classname)-meta.pd */ + libdir_get_fullname(fullclassname, FILENAME_MAX, classname); + + if ((fd = sys_trytoopenone(path, fullclassname, ".pd", + dirbuf, &nameptr, FILENAME_MAX, 0)) < 0) { + return 0; } + sys_close(fd); + if(libdir_add_to_path(dirbuf, canvas)) + logpost(NULL, 3, "libdir_loader: added '%s' to the %s objectclass path", + classname, canvas?"canvas-local":"global"); + /* post("libdir_loader loaded fullclassname: '%s'\n", fullclassname); */ - if (sys_verbose) - post("Loaded libdir '%s' from '%s'", classname, dirbuf); +#if 0 + logpost(NULL, 14, "Loaded libdir '%s' from '%s'", classname, dirbuf); +#endif return (1); } +static t_class *libdir_class; +static void*libdir_new(void) +{ + t_pd *x = pd_new(libdir_class); + return (x); +} + void libdir_setup(void) { -/* relies on t.grill's loader functionality, fully added in 0.40 */ - sys_register_loader(libdir_loader); - post("libdir loader %s",version); - post("\tcompiled on "__DATE__" at "__TIME__ " "); - post("\tcompiled against Pd version %d.%d.%d.%s", PD_MAJOR_VERSION, - PD_MINOR_VERSION, PD_BUGFIX_VERSION, PD_TEST_VERSION); + int major, minor, bugfix; + sys_getversion(&major, &minor, &bugfix); + if (major>0 || minor >=47) { + sys_register_loader(libdir_loader_pathwise); + } else { + sys_register_loader(libdir_loader_legacy); + } + logpost(NULL, 3, "libdir loader %s",version); + logpost(NULL, 3, "\tcompiled on "__DATE__" at "__TIME__ " "); + logpost(NULL, 3, "\tcompiled against Pd version %d.%d.%d.%s", + PD_MAJOR_VERSION, PD_MINOR_VERSION, PD_BUGFIX_VERSION, PD_TEST_VERSION); + libdir_class = class_new(gensym("libdir"), libdir_new, 0, sizeof(t_object), CLASS_NOINLET, 0); +} + +void setup(void) +{ + libdir_setup(); } diff --git a/pd/src/m_class.c b/pd/src/m_class.c index 3db693bbb80cc29615b8cb4cc54dc2b9774c337d..f5926e98e93f02b65662fbc48801fa94954f60f5 100644 --- a/pd/src/m_class.c +++ b/pd/src/m_class.c @@ -599,6 +599,7 @@ static t_symbol *addfileextent(t_symbol *s) return (gensym(namebuf)); } +#define MAXOBJDEPTH 1000 static int tryingalready; void canvas_popabstraction(t_canvas *x); @@ -613,39 +614,20 @@ int pd_setloadingabstraction(t_symbol *sym); doesn't know. Pd tries to load it as an extern, then as an abstraction. */ void new_anything(void *dummy, t_symbol *s, int argc, t_atom *argv) { - t_pd *current; - int fd; - char dirbuf[FILENAME_MAX], *nameptr; - if (tryingalready) return; + if (tryingalready>MAXOBJDEPTH){ + error("maximum object loading depth %d reached", MAXOBJDEPTH); + return; + } newest = 0; class_loadsym = s; if (sys_load_lib(canvas_getcurrent(), s->s_name)) { - tryingalready = 1; + tryingalready++; typedmess(dummy, s, argc, argv); - tryingalready = 0; + tryingalready--; return; } class_loadsym = 0; - current = s__X.s_thing; - if ((fd = canvas_open(canvas_getcurrent(), s->s_name, ".pd", - dirbuf, &nameptr, FILENAME_MAX, 0)) >= 0 || - (fd = canvas_open(canvas_getcurrent(), s->s_name, ".pat", - dirbuf, &nameptr, FILENAME_MAX, 0)) >= 0) - { - close (fd); - if (!pd_setloadingabstraction(s)) - { - canvas_setargs(argc, argv); - binbuf_evalfile(gensym(nameptr), gensym(dirbuf)); - canvas_initbang((t_canvas *)(s__X.s_thing));/* JMZ*/ - if (s__X.s_thing != current) - canvas_popabstraction((t_canvas *)(s__X.s_thing)); - canvas_setargs(0, 0); - } - else error("%s: can't load abstraction within itself\n", s->s_name); - } - else newest = 0; } t_symbol s_pointer = {"pointer", 0, 0}; diff --git a/pd/src/m_pd.h b/pd/src/m_pd.h index ea91ccea7c36644860d4bf4b494b1e6334bc60de..5583c5c4cd929057d043d7ce5bfaed30ae5be89c 100644 --- a/pd/src/m_pd.h +++ b/pd/src/m_pd.h @@ -11,8 +11,8 @@ extern "C" { #include <stdarg.h> #define PD_MAJOR_VERSION 0 -#define PD_MINOR_VERSION 43 -#define PD_BUGFIX_VERSION 10 +#define PD_MINOR_VERSION 47 +#define PD_BUGFIX_VERSION 0 #define PD_TEST_VERSION "" #define PD_L2ORK_VERSION "2.0" #define PDL2ORK diff --git a/pd/src/s_loader.c b/pd/src/s_loader.c index 23140ccd94a80c13b4bbfe66c1fb1fe5a3fb890f..fe1304c63155578b5f1be4f2ee34de2b454be482 100644 --- a/pd/src/s_loader.c +++ b/pd/src/s_loader.c @@ -2,20 +2,20 @@ * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ -#ifdef HAVE_LIBDL +#if defined(HAVE_LIBDL) || defined(__FreeBSD__) #include <dlfcn.h> #endif -#ifdef UNISTD +#ifdef HAVE_UNISTD_H #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #endif -#ifdef MSW +#ifdef _WIN32 #include <io.h> #include <windows.h> #endif #ifdef __APPLE__ -#include <mach-o/dyld.h> +#include <mach-o/dyld.h> #endif #include <string.h> #include "m_pd.h" @@ -24,6 +24,7 @@ #include <sys/stat.h> #ifdef _MSC_VER /* This is only for Microsoft's compiler, not cygwin, e.g. */ #define snprintf sprintf_s +#define stat _stat #endif typedef void (*t_xxx)(void); @@ -36,9 +37,7 @@ objects. The specific name is the letter b, l, d, or m for BSD, linux, darwin, or microsoft, followed by a more specific string, either "fat" for a fat binary or an indication of the instruction set. */ -#ifdef __FreeBSD__ -static char sys_dllextent[] = ".b_i386", sys_dllextent2[] = ".pd_freebsd"; -#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__GNU__) +#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__GNU__) || defined(__FreeBSD__) static char sys_dllextent2[] = ".pd_linux"; # ifdef __x86_64__ static char sys_dllextent[] = ".l_ia64"; // this should be .l_x86_64 or .l_amd64 @@ -69,7 +68,7 @@ typedef struct _loadedlist } t_loadlist; static t_loadlist *sys_loaded; -int sys_onloadlist(char *classname) /* return true if already loaded */ +int sys_onloadlist(const char *classname) /* return true if already loaded */ { t_symbol *s = gensym(classname); t_loadlist *ll; @@ -79,7 +78,8 @@ int sys_onloadlist(char *classname) /* return true if already loaded */ return (0); } -void sys_putonloadlist(char *classname) /* add to list of loaded modules */ + /* add to list of loaded modules */ +void sys_putonloadlist(const char *classname) { t_loadlist *ll = (t_loadlist *)getbytes(sizeof(*ll)); ll->ll_name = gensym(classname); @@ -90,27 +90,31 @@ void sys_putonloadlist(char *classname) /* add to list of loaded modules */ void class_set_extern_dir(t_symbol *s); -static int sys_do_load_lib(t_canvas *canvas, char *objectname) +static int sys_do_load_abs(t_canvas *canvas, const char *objectname, + const char *path); +static int sys_do_load_lib(t_canvas *canvas, const char *objectname, + const char *path) { - char symname[MAXPDSTRING], filename[FILENAME_MAX], dirbuf[FILENAME_MAX], - *classname, *nameptr; + char symname[MAXPDSTRING], filename[MAXPDSTRING], dirbuf[MAXPDSTRING], + *nameptr, altsymname[MAXPDSTRING]; + const char *classname, *cnameptr; void *dlobj; t_xxx makeout = NULL; int i, hexmunge = 0, fd; -#ifdef MSW +#ifdef _WIN32 HINSTANCE ntdll; #endif - if (classname = strrchr(objectname, '/')) + /* NULL-path is only used as a last resort, + but we have already tried all paths */ + if(!path)return (0); + + if ((classname = strrchr(objectname, '/'))) classname++; else classname = objectname; - if (sys_onloadlist(objectname)) + for (i = 0, cnameptr = classname; i < MAXPDSTRING-7 && *cnameptr; + cnameptr++) { - post("%s: already loaded", objectname); - return (1); - } - for (i = 0, nameptr = classname; i < MAXPDSTRING-7 && *nameptr; nameptr++) - { - char c = *nameptr; + char c = *cnameptr; if ((c>='0' && c<='9') || (c>='A' && c<='Z')|| (c>='a' && c<='z' )|| c == '_') { @@ -118,7 +122,7 @@ static int sys_do_load_lib(t_canvas *canvas, char *objectname) i++; } /* trailing tilde becomes "_tilde" */ - else if (c == '~' && nameptr[1] == 0) + else if (c == '~' && cnameptr[1] == 0) { strcpy(symname+i, "_tilde"); i += strlen(symname+i); @@ -137,76 +141,112 @@ static int sys_do_load_lib(t_canvas *canvas, char *objectname) strncpy(symname, "setup_", 6); } else strcat(symname, "_setup"); - + #if 0 - fprintf(stderr, "lib: %s %s\n", classname, symname); + fprintf(stderr, "lib: %s\n", classname); #endif /* try looking in the path for (objectname).(sys_dllextent) ... */ - if ((fd = canvas_open(canvas, objectname, sys_dllextent, - dirbuf, &nameptr, FILENAME_MAX, 1)) >= 0) + if ((fd = sys_trytoopenone(path, objectname, sys_dllextent, + dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0) goto gotone; /* same, with the more generic sys_dllextent2 */ - if ((fd = canvas_open(canvas, objectname, sys_dllextent2, - dirbuf, &nameptr, FILENAME_MAX, 1)) >= 0) + if ((fd = sys_trytoopenone(path, objectname, sys_dllextent2, + dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0) goto gotone; /* next try (objectname)/(classname).(sys_dllextent) ... */ - strncpy(filename, objectname, FILENAME_MAX); + strncpy(filename, objectname, MAXPDSTRING); filename[MAXPDSTRING-2] = 0; strcat(filename, "/"); - strncat(filename, classname, FILENAME_MAX-strlen(filename)); - filename[FILENAME_MAX-1] = 0; - if ((fd = canvas_open(canvas, filename, sys_dllextent, - dirbuf, &nameptr, FILENAME_MAX, 1)) >= 0) + strncat(filename, classname, MAXPDSTRING-strlen(filename)); + filename[MAXPDSTRING-1] = 0; + if ((fd = sys_trytoopenone(path, filename, sys_dllextent, + dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0) goto gotone; - if ((fd = canvas_open(canvas, filename, sys_dllextent2, - dirbuf, &nameptr, FILENAME_MAX, 1)) >= 0) + if ((fd = sys_trytoopenone(path, filename, sys_dllextent2, + dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0) goto gotone; +#ifdef ANDROID + /* Android libs have a 'lib' prefix, '.so' suffix and don't allow ~ */ + char libname[MAXPDSTRING] = "lib"; + strncat(libname, objectname, MAXPDSTRING - 4); + int len = strlen(libname); + if (libname[len-1] == '~' && len < MAXPDSTRING - 6) { + strcpy(libname+len-1, "_tilde"); + } + if ((fd = sys_trytoopenone(path, libname, ".so", + dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0) + goto gotone; +#endif return (0); gotone: close(fd); class_set_extern_dir(gensym(dirbuf)); /* rebuild the absolute pathname */ - strncpy(filename, dirbuf, FILENAME_MAX); - filename[FILENAME_MAX-2] = 0; + strncpy(filename, dirbuf, MAXPDSTRING); + filename[MAXPDSTRING-2] = 0; strcat(filename, "/"); - strncat(filename, nameptr, FILENAME_MAX-strlen(filename)); - filename[FILENAME_MAX-1] = 0; + strncat(filename, nameptr, MAXPDSTRING-strlen(filename)); + filename[MAXPDSTRING-1] = 0; -#ifdef HAVE_LIBDL +#ifdef _WIN32 + { + char dirname[MAXPDSTRING], *s, *basename; + sys_bashfilename(filename, filename); + /* set the dirname as DllDirectory, meaning in the path for + loading other DLLs so that dependent libraries can be included + in the same folder as the external. SetDllDirectory() needs a + minimum supported version of Windows XP SP1 for + SetDllDirectory, so WINVER must be 0x0502 */ + strncpy(dirname, filename, MAXPDSTRING); + s = strrchr(dirname, '\\'); + basename = s; + if (s && *s) + *s = '\0'; + if (!SetDllDirectory(dirname)) + error("Could not set '%s' as DllDirectory(), '%s' might not load.", + dirname, basename); + /* now load the DLL for the external */ + ntdll = LoadLibrary(filename); + if (!ntdll) + { + verbose(1, "%s: couldn't load", filename); + class_set_extern_dir(&s_); + return (0); + } + makeout = (t_xxx)GetProcAddress(ntdll, symname); + if (!makeout) + makeout = (t_xxx)GetProcAddress(ntdll, "setup"); + SetDllDirectory(NULL); /* reset DLL dir to nothing */ + } +#elif defined(HAVE_LIBDL) || defined(__FreeBSD__) dlobj = dlopen(filename, RTLD_NOW | RTLD_GLOBAL); if (!dlobj) { - post("%s: %s", filename, dlerror()); + verbose(1, "%s: %s", filename, dlerror()); class_set_extern_dir(&s_); return (0); } makeout = (t_xxx)dlsym(dlobj, symname); -#endif -#ifdef MSW - sys_bashfilename(filename, filename); - ntdll = LoadLibrary(filename); - if (!ntdll) - { - post("%s: couldn't load", filename); - class_set_extern_dir(&s_); - return (0); - } - makeout = (t_xxx)GetProcAddress(ntdll, symname); + if(!makeout) + makeout = (t_xxx)dlsym(dlobj, "setup"); +#else +#warning "No dynamic loading mechanism specified, \ + libdl or WIN32 required for loading externals!" #endif if (!makeout) { - post("load_object: Symbol \"%s\" not found", symname); + verbose(1, "load_object: Symbol \"%s\" not found", symname); class_set_extern_dir(&s_); return 0; } (*makeout)(); class_set_extern_dir(&s_); - sys_putonloadlist(objectname); return (1); } + /* linked list of loaders */ typedef struct loader_queue { loader_t loader; @@ -221,7 +261,9 @@ void sys_register_loader(loader_t loader) loader_queue_t *q = &loaders; while (1) { - if (q->next) + if (q->loader == loader) /* already loaded - nothing to do */ + return; + else if (q->next) q = q->next; else { @@ -230,18 +272,78 @@ void sys_register_loader(loader_t loader) q->next->next = NULL; break; } - } + } } -int sys_load_lib(t_canvas *canvas, char *classname) +#include "g_canvas.h" + +/* the data passed to the iter-function */ +struct _loadlib_data +{ + t_canvas *canvas; + const char *classname; + int ok; +}; + +int sys_loadlib_iter(const char *path, struct _loadlib_data *data) { - int dspstate = canvas_suspend_dsp(); int ok = 0; loader_queue_t *q; for(q = &loaders; q; q = q->next) - if (ok = q->loader(canvas, classname)) break; + if ((ok = q->loader(data->canvas, data->classname, path))) + break; + /* if all loaders failed, try to load as abstraction */ + if (!ok) + ok = sys_do_load_abs(data->canvas, data->classname, path); + data->ok = ok; + return (ok == 0); +} + +int sys_load_lib(t_canvas *canvas, const char *classname) +{ + int dspstate = canvas_suspend_dsp(); + struct _loadlib_data data; + data.canvas = canvas; + data.ok = 0; + + if (sys_onloadlist(classname)) + { + verbose(1, "%s: already loaded", classname); + return (1); + } + /* if classname is absolute, try this first */ + if (sys_isabsolutepath(classname)) + { + /* this is just copied from sys_open_absolute() + LATER avoid code duplication */ + char dirbuf[MAXPDSTRING], *z = strrchr(classname, '/'); + int dirlen; + if (!z) + return (0); + dirlen = z - classname; + if (dirlen > MAXPDSTRING-1) + dirlen = MAXPDSTRING-1; + strncpy(dirbuf, classname, dirlen); + dirbuf[dirlen] = 0; + data.classname=classname+(dirlen+1); + sys_loadlib_iter(dirbuf, &data); + } + data.classname = classname; + if(!data.ok) + canvas_path_iterate(canvas, (t_canvas_path_iterator)sys_loadlib_iter, + &data); + + /* if loaders failed so far, we try a last time without a PATH + * let the loaders search wherever they want */ + if (!data.ok) + sys_loadlib_iter(0, &data); + + if(data.ok) + sys_putonloadlist(classname); + + canvas_resume_dsp(dspstate); - return ok; + return data.ok; } int sys_run_scheduler(const char *externalschedlibname, @@ -250,41 +352,142 @@ int sys_run_scheduler(const char *externalschedlibname, typedef int (*t_externalschedlibmain)(const char *); t_externalschedlibmain externalmainfunc; char filename[MAXPDSTRING]; + struct stat statbuf; snprintf(filename, sizeof(filename), "%s%s", externalschedlibname, sys_dllextent); sys_bashfilename(filename, filename); -#ifdef MSW + /* if first-choice file extent can't 'stat', go for second */ + if (stat(filename, &statbuf) < 0) + { + snprintf(filename, sizeof(filename), "%s%s", externalschedlibname, + sys_dllextent2); + sys_bashfilename(filename, filename); + } +#ifdef _WIN32 { HINSTANCE ntdll = LoadLibrary(filename); if (!ntdll) { - post("%s: couldn't load external scheduler lib ", filename); + fprintf(stderr, "%s: couldn't load external scheduler\n", filename); + error("%s: couldn't load external scheduler", filename); return (1); } externalmainfunc = - (t_externalschedlibmain)GetProcAddress(ntdll, "main"); + (t_externalschedlibmain)GetProcAddress(ntdll, "pd_extern_sched"); + if (!externalmainfunc) + externalmainfunc = + (t_externalschedlibmain)GetProcAddress(ntdll, "main"); } -#else +#elif defined HAVE_LIBDL { void *dlobj; - struct stat statbuf; - /* if first-choice file extent can't 'stat', go for second */ - if (stat(filename, &statbuf) < 0) - { - snprintf(filename, sizeof(filename), "%s%s", externalschedlibname, - sys_dllextent2); - sys_bashfilename(filename, filename); - } dlobj = dlopen(filename, RTLD_NOW | RTLD_GLOBAL); if (!dlobj) { - post("%s: %s", filename, dlerror()); + error("%s: %s", filename, dlerror()); fprintf(stderr, "dlopen failed for %s: %s\n", filename, dlerror()); return (1); } externalmainfunc = (t_externalschedlibmain)dlsym(dlobj, "pd_extern_sched"); } +#else + return (0); #endif - return((*externalmainfunc)(sys_extraflagsstring)); + if (externalmainfunc) + return((*externalmainfunc)(sys_extraflagsstring)); + else + { + fprintf(stderr, "%s: couldn't find pd_extern_sched() or main()\n", + filename); + return (0); + } +} + + +/* abstraction loading */ +void canvas_popabstraction(t_canvas *x); +int pd_setloadingabstraction(t_symbol *sym); +extern t_pd *newest; + +static t_pd *do_create_abstraction(t_symbol*s, int argc, t_atom *argv) +{ + /* + * TODO: check if the there is a binbuf cached for <canvas::symbol> + and use that instead. We'll have to invalidate the cache once we + are done (either with a clock_delay(0) or something else) + */ + if (!pd_setloadingabstraction(s)) + { + const char *objectname = s->s_name; + char dirbuf[MAXPDSTRING], classslashclass[MAXPDSTRING], *nameptr; + t_glist *glist = (t_glist *)canvas_getcurrent(); + t_canvas *canvas = (t_canvas*)glist_getcanvas(glist); + int fd = -1; + + t_pd *was = s__X.s_thing; + snprintf(classslashclass, MAXPDSTRING, "%s/%s", objectname, objectname); + if ((fd = canvas_open(canvas, objectname, ".pd", + dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0 || + (fd = canvas_open(canvas, objectname, ".pat", + dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0 || + (fd = canvas_open(canvas, classslashclass, ".pd", + dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0) + { + close(fd); + canvas_setargs(argc, argv); + + binbuf_evalfile(gensym(nameptr), gensym(dirbuf)); + if (s__X.s_thing && was != s__X.s_thing) + canvas_popabstraction((t_canvas *)(s__X.s_thing)); + else s__X.s_thing = was; + canvas_setargs(0, 0); + return (newest); + } + /* otherwise we couldn't do it; just return 0 */ + } + else error("%s: can't load abstraction within itself\n", s->s_name); + newest = 0; + return (0); +} + +/* search for abstraction; register a creator if found */ +static int sys_do_load_abs(t_canvas *canvas, const char *objectname, + const char *path) +{ + int fd; + static t_gobj*abstraction_classes = 0; + char dirbuf[MAXPDSTRING], classslashclass[MAXPDSTRING], *nameptr; + /* NULL-path is only used as a last resort, + but we have already tried all paths */ + if (!path) return (0); + + snprintf(classslashclass, MAXPDSTRING, "%s/%s", objectname, objectname); + if ((fd = sys_trytoopenone(path, objectname, ".pd", + dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0 || + (fd = sys_trytoopenone(path, objectname, ".pat", + dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0 || + (fd = sys_trytoopenone(path, classslashclass, ".pd", + dirbuf, &nameptr, MAXPDSTRING, 1)) >= 0) + { + t_class*c=0; + close(fd); + /* found an abstraction, now register it as a new pseudo-class */ + class_set_extern_dir(gensym(dirbuf)); + if((c=class_new(gensym(objectname), + (t_newmethod)do_create_abstraction, 0, + 0, 0, A_GIMME, 0))) + { + /* store away the newly created class, maybe we will need it one day */ + t_gobj*absclass=0; + absclass=t_getbytes(sizeof(*absclass)); + absclass->g_pd=c; + absclass->g_next=abstraction_classes; + abstraction_classes=absclass; + } + class_set_extern_dir(&s_); + + return (1); + } + return (0); } diff --git a/pd/src/s_stuff.h.in b/pd/src/s_stuff.h.in index a5b72e89aaa8b8ac2ff2cbdb43df9dc0655d9a0b..0fb85a58a9024504c115fb3d5655f23cad1b2890 100644 --- a/pd/src/s_stuff.h.in +++ b/pd/src/s_stuff.h.in @@ -71,8 +71,8 @@ extern t_symbol *sys_libdir; /* library directory for auxilliary files */ extern t_symbol *sys_guidir; /* directory holding pd_gui, u_pdsend, etc */ /* s_loader.c */ -typedef int (*loader_t)(t_canvas *canvas, char *classname); /* callback type */ -EXTERN int sys_load_lib(t_canvas *canvas, char *filename); +typedef int (*loader_t)(t_canvas *canvas, const char *classname, const char *path); /* callback type */ +EXTERN int sys_load_lib(t_canvas *canvas, const char *classname); EXTERN void sys_register_loader(loader_t loader); /* s_audio.c */