Merge commit '77525265694bac50ed94c5ef62ebbae680c72ab0'

This commit is contained in:
IOhannes m zmölnig 2024-09-26 16:07:53 +02:00
commit f2ed62ce3f
56 changed files with 993 additions and 128 deletions

View file

@ -1,5 +1,13 @@
Changelog for Makefile.pdlibbuilder.
v0.7.0, dated 2023-07-06
- build double-precision externals with the 'floatsize' variable
- allow building multiple flavours of an external side-by-side (#78)
- facilitate multiple platform co-installation of shared lib (#58)
- fix use of shared.ldflags with helper-library (#64)
- fix broken armv6l platform detection (#71)
- improve documentation
v0.6.0, dated 2019-12-21
- detect target platform (OS and architecture) rather than build platform (#55)
- introduce optional user variable 'PLATFORM' for cross compilation

View file

@ -1,5 +1,5 @@
# Makefile.pdlibbuilder dated 2019-12-21
version = 0.6.0
version = 0.7.0
# Helper makefile for Pure Data external libraries.
# Written by Katja Vetter March-June 2015 for the public domain. No warranties.
@ -102,6 +102,8 @@ version = 0.6.0
# Optional user variables for make command line or environment:
#
# - PLATFORM
# - extension
# - floatsize
#
# Deprecated path variables:
#
@ -205,6 +207,19 @@ version = 0.6.0
# will then be autodefined accordingly. In most cases no other variables need to
# be overridden.
#
# extension:
# Extension for the external to use. Example: m_amd64
# A sane default is picked, but it is useful if you want to provide
# co-installable externals for multiple platforms (for the same operating
# systems)
#
# floatsize:
# the size of the t_float in bits. Example: 32
# t_float are usually single precision (32bit), which is the default.
# For double precision use floatsize=64
# When building double precision externals, you will want to set the extension
# as well, e.g. extension=windows-amd64-64.dll (<system>-<cpu>-<floatsize>.<ext>)
#
# CPPFLAGS:
# Preprocessor flags which are not strictly required for building.
#
@ -369,78 +384,6 @@ externalsdir ?= ..
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
################################################################################
### target platform detection ##################################################
################################################################################
@ -514,6 +457,14 @@ target.arch := $(firstword $(target.triplet))
### variables per platform #####################################################
################################################################################
#=== flags per floatsize == ====================================================
floatsize = 32
ifneq ($(filter-out 32,$(floatsize)),)
floatsize.flags = -DPD_FLOATSIZE=$(floatsize)
else
floatsize.flags =
endif
#=== flags per architecture ====================================================
@ -523,7 +474,7 @@ target.arch := $(firstword $(target.triplet))
# $ gcc -Q --help=target
# ARMv6: Raspberry Pi 1st gen, not detectable from target.arch
ifeq ($(shell uname), armv6l)
ifeq ($(shell uname -m), armv6l)
arch.c.flags = -march=armv6 -mfpu=vfp -mfloat-abi=hard
# ARMv7: Beagle, Udoo, RPi2 etc.
@ -565,7 +516,7 @@ ifeq ($(system), Linux)
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)
shared.ldflags = -rdynamic -fPIC -shared -Wl,-soname,$(shared.lib)
endif
@ -656,14 +607,14 @@ ifeq ($(system), Windows)
extension = dll
c.flags :=
c.ldflags := -static-libgcc -shared \
-Wl,--enable-auto-import "$(PDBINDIR)/pd.dll"
-Wl,--enable-auto-import "$(PDBINDIR)/pd$(filter-out 32,$(floatsize)).dll"
c.ldlibs :=
cxx.flags := -fcheck-new
cxx.ldflags := -static-libgcc -static-libstdc++ -shared \
-Wl,--enable-auto-import "$(PDBINDIR)/pd.dll"
-Wl,--enable-auto-import "$(PDBINDIR)/pd$(filter-out 32,$(floatsize)).dll"
cxx.ldlibs :=
shared.extension = dll
shared.ldflags := -static-libgcc -shared "$(PDBINDIR)/pd.dll"
shared.ldflags := -static-libgcc -shared "$(PDBINDIR)/pd$(filter-out 32,$(floatsize)).dll"
stripflags = --strip-all
endif
@ -711,7 +662,7 @@ endif
CFLAGS = $(warn.flags) $(optimization.flags) $(arch.c.flags)
# preprocessor flags
cpp.flags := -DPD -I "$(PDINCLUDEDIR)" $(cpp.flags) $(CPPFLAGS)
cpp.flags := -DPD -I "$(PDINCLUDEDIR)" $(floatsize.flags) $(cpp.flags) $(CPPFLAGS)
# flags for dependency checking (cflags from makefile may define -I options)
depcheck.flags := $(cpp.flags) $(cflags)
@ -720,7 +671,7 @@ depcheck.flags := $(cpp.flags) $(cflags)
LDFLAGS := $(arch.ld.flags)
# now add the same ld flags to shared dynamic lib
shared.ldflags := $(shared.ldflags) $(LDFLAGS)
shared.ldflags += $(LDFLAGS)
# accumulated flags for C compiler / linker
c.flags := $(cpp.flags) $(c.flags) $(cflags) $(CFLAGS)
@ -733,6 +684,84 @@ cxx.ldflags := $(cxx.ldflags) $(ldflags) $(LDFLAGS)
cxx.ldlibs := $(cxx.ldlibs) $(ldlibs)
################################################################################
### 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 ###########################################################
################################################################################
object.extension = $(extension).o
#=== 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 .$(object.extension), $(basename $(classes.sources)))
common.objects := $(addsuffix .$(object.extension), $(basename $(common.sources)))
shared.objects := $(addsuffix .$(object.extension), $(basename $(shared.sources)))
lib.setup.objects := $(addsuffix .$(object.extension), $(basename $(lib.setup.sources)))
all.objects = $(classes.objects) $(common.objects) $(shared.objects) \
$(lib.setup.objects)
#=== executables ===============================================================
# construct class executable names from class names
classes.executables := $(addsuffix .$(extension), $(classes))
# Construct shared lib executable name if shared sources are defined.
# If extension does not end with shared extension, use both to facilitate co-
# installation for different platforms, like .m_i386.dll and .linux-amd64-32.so
ifdef shared.sources
ifneq ($(filter %.$(shared.extension), .$(extension)), )
# $(extension) already ends with $(shared.extension), no need to duplicate it
shared.lib = lib$(lib.name).$(extension)
else
shared.lib = lib$(lib.name).$(extension).$(shared.extension)
endif
else
shared.lib :=
endif
################################################################################
### variables: tools ###########################################################
################################################################################
@ -786,7 +815,7 @@ endif
# store path to pd.dll; if not found, ls will give a useful error
ifeq ($(system), Windows)
pddll := $(shell ls "$(PDBINDIR)/pd.dll")
pddll := $(shell ls "$(PDBINDIR)/pd$(filter-out 32,$(floatsize)).dll")
endif
# when making target all, check if m_pd.h is found and print info about it
@ -870,8 +899,8 @@ define link-class
$(compile-$1) \
$($1.ldflags) $($2.class.ldflags) \
-o $2.$(extension) \
$(addsuffix .o, $(basename $($2.class.sources))) \
$(addsuffix .o, $(basename $(common.sources))) \
$(addsuffix .$(object.extension), $(basename $($2.class.sources))) \
$(addsuffix .$(object.extension), $(basename $(common.sources))) \
$($1.ldlibs) $($2.class.ldlibs) $(shared.lib)
endef
@ -917,13 +946,13 @@ endif
define link-shared
$(compile-$1) \
$(shared.ldflags) \
-o lib$(lib.name).$(shared.extension) $(shared.objects) \
-o $(shared.lib) $(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)
$(shared.lib): $(shared.objects)
$(info ++++ info: linking objects in shared lib $@)
$(if $(filter %.cc %.cpp, $(shared.sources)), \
$(call link-shared,cxx), \
@ -945,13 +974,13 @@ endef
# Three rules to create .o files. These are double colon 'terminal' rules,
# meaning they are the last in a rules chain.
%.o:: %.c
%.$(object.extension):: %.c
$(call make-object-file,c)
%.o:: %.cc
%.$(object.extension):: %.cc
$(call make-object-file,cxx)
%.o:: %.cpp
%.$(object.extension):: %.cpp
$(call make-object-file,cxx)
@ -970,8 +999,8 @@ 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)))
$v.$(extension): $(addsuffix .$(object.extension), $(basename $($v.class.sources))) \
$(addsuffix .$(object.extension), $(basename $(common.sources)))
endef
# evaluate explicit prerequisite rules for all classes
@ -1001,7 +1030,7 @@ endif
# 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)
$(dir $1)$(patsubst %.o:,%.$(object.extension):,$(filter %.o: %.h, $(shell $(CPP) $(depcheck.flags) -MM $1))) $(MAKEFILE_LIST)
endef
# evaluate implicit prerequisite rules when rebuilding everything

View file

@ -35,7 +35,8 @@ Makefile.pdlibbuilder at the end of the Makefile. Like so:
datafiles = myclass1-help.pd myclass2-help.pd README.txt LICENSE.txt
include Makefile.pdlibbuilder
PDLIBBUILDER_DIR=.
include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder
For files in class.sources it is assumed that class name == source file
@ -68,6 +69,43 @@ Root directory for installation of Pd library directories. Overrides the
default install location.
### platform detection and predefined variables ###
Makefile.pdlibbuilder tries to detect architecture and operating system in
order to define platform-specific variables. Since v0.6.0 we let the compiler
report target platform, rather than taking the build machine as reference. This
simplifies cross compilation. The kind of build options that are predefined:
- optimizations useful for realtime DSP processing
- options strictly required for the platform
- options to make the build work accross a range of CPU's and OS versions
The exact choice and definition predefined variables changes over time, as new
platforms arrive and older platforms become obsolete. The easiest way to get an
overview for your platform is by checking the flags categories in the output of
target `vars`. Variables written in capitals (like `CFLAGS`) are intentionally
exposed as user variables, although technically all makefile variables can be
overridden by make command arguments.
### specific language versions ###
Makefile.pdlibbuilder handles C and C++, but can not detect if your code uses
features of a specific version (like C99, C++11, C++14 etc.). In such cases
your makefile should specify that version as compiler option:
cflags = -std=c++11
Also you may need to be explicit about minimum OSX version. For example, C++11
needs OSX 10.9 or higher:
define forDarwin
cflags = -mmacosx-version-min=10.9
endef
### documentation ###

View file

@ -0,0 +1,15 @@
# recursively build all example projects in the subdirectories
makefiledirs := $(filter-out _%, $(dir $(wildcard */Makefile)))
PDLIBBUILDER_DIR = ../
include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder
buildcheck installcheck: $(makefiledirs)
runcheck:
PDBINDIR=$(PDBINDIR) ./test-patches.sh $(makefiledirs:%=%*.pd)
projects:
@echo $(makefiledirs)

View file

@ -0,0 +1,24 @@
# Makefile to build class '_template_' for Pure Data.
# Needs Makefile.pdlibbuilder as helper makefile for platform-dependent build
# settings and rules.
# library name
lib.name = _template_
# input source file (class name == source file basename)
class.sources = _template_.c
# all extra files to be included in binary distribution of the library
datafiles = _template_-help.pd _template_-meta.pd
# include Makefile.pdlibbuilder
# (for real-world projects see the "Project Management" section
# in tips-tricks.md)
PDLIBBUILDER_DIR=../..
include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder
# simplistic tests whether all expected files have been produced/installed
buildcheck: all
test -e _template_.$(extension)
installcheck: install
test -e $(installpath)/_template_.$(extension)

View file

@ -0,0 +1,4 @@
#N canvas 335 160 450 300 12;
#X obj 143 125 _template_;
#X msg 143 93 7;
#X connect 1 0 0 0;

View file

@ -0,0 +1,9 @@
#N canvas 966 322 200 200 10;
#N canvas 19 51 420 300 META 0;
#X text 10 10 META this is a prototype of a libdir meta file;
#X text 10 51 AUTHOR IOhannes m zmolnig;
#X text 10 110 VERSION 1.0.0;
#X text 10 90 LICENSE CC0;
#X text 10 70 DESCRIPTION Example "_template_" external.;
#X text 10 30 NAME _template_;
#X restore 10 10 pd META;

View file

@ -0,0 +1,13 @@
#include <m_pd.h>
t_class*_template__class;
static void _template__float(t_object*x, t_float f1) {
pd_error(x, "%s got %f", __FUNCTION__, f1);
}
static void*_template__new(void) {
return pd_new(_template__class);
}
void _template__setup(void) {
post("%s", __FUNCTION__);
_template__class = class_new(gensym("_template_"), _template__new, 0, sizeof(t_object), 0, A_NULL);
class_addfloat(_template__class, _template__float);
}

View file

@ -0,0 +1,46 @@
#!/bin/sh
template=_template_
template_dir=${0%/*}/${template}
outdir=$1
outdir=${outdir%/}
outname=${outdir##*/}
usage() {
cat 1>&2 <<EOL
usage: $0 <outdir>
creates a new test-directory <outdir> from _template_;
<outdir> must not exist yet.
EOL
if [ "x$@" != "x" ]; then
echo
echo " $@" 1>&2
fi
exit 1
}
if [ "x${outdir}" = "x" ]; then
usage
fi
if [ -d "${outdir}" ]; then
usage "output directory '${outdir}' already exists!"
fi
if [ ! -d "${template_dir}" ]; then
echo "unable to find '${template_dir}'" 1>&2
exit 1
fi
mkdir -p "${outdir}" || usage "unable to create '${outdir}'!"
rmdir "${outdir}"
cp -r "${template_dir}" "${outdir}"
find "${outdir}" -type f -exec sed -e "s|${template}|${outname}|g" -i {} +
for f in "${outdir}"/*; do
g=$(echo $f | sed -e "s|${template}|${outname}|g")
if [ "x${f}" != "x${g}" ]; then
mv "${f}" "${g}"
fi
done

View file

@ -0,0 +1,39 @@
# Makefile to build class 'multifor' for Pure Data.
# Needs Makefile.pdlibbuilder as helper makefile for platform-dependent build
# settings and rules.
# library name
lib.name = multifor
# input source file (class name == source file basename)
class.sources = multiforA.c
# additional classes
define forLinux
class.sources += multiforB.c
endef
define forDarwin
class.sources += multiforB.c
endef
define forWindows
class.sources += multiforB.c
endef
# all extra files to be included in binary distribution of the library
datafiles = multifor-help.pd multifor-meta.pd
# include Makefile.pdlibbuilder
# (for real-world projects see the "Project Management" section
# in tips-tricks.md)
PDLIBBUILDER_DIR=../..
include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder
# simplistic tests whether all expected files have been produced/installed
buildcheck: all
test -e multiforA.$(extension)
test -e multiforB.$(extension)
installcheck: install
test -e $(installpath)/multiforA.$(extension)
test -e $(installpath)/multiforB.$(extension)
test -e $(installpath)/multifor-help.pd
test -e $(installpath)/multifor-meta.pd

View file

@ -0,0 +1,9 @@
multifor
========
minimal pd-lib-builder project that shows how to compile
a library that contains multiple C-files that are compiled into
multiple binaries each containing a different Pd-objectclass.
some of the objectclasses are only compiled on specific platforms.
this is a special case of the one-object-per-binary library structure.

View file

@ -0,0 +1,7 @@
#N canvas 335 160 450 300 12;
#X msg 143 93 7;
#X obj 143 125 multiforA;
#X obj 223 125 multiforB;
#X msg 223 93 12;
#X connect 0 0 1 0;
#X connect 3 0 2 0;

View file

@ -0,0 +1,9 @@
#N canvas 966 322 200 200 10;
#N canvas 19 51 420 300 META 0;
#X text 10 10 META this is a prototype of a libdir meta file;
#X text 10 51 AUTHOR IOhannes m zmolnig;
#X text 10 110 VERSION 1.0.0;
#X text 10 90 LICENSE CC0;
#X text 10 70 DESCRIPTION Example "multifor" external.;
#X text 10 30 NAME multifor;
#X restore 10 10 pd META;

View file

@ -0,0 +1,13 @@
#include <m_pd.h>
t_class*multiforA_class;
static void multiforA_float(t_object*x, t_float f1) {
pd_error(x, "%s got %f", __FUNCTION__, f1);
}
static void*multiforA_new(void) {
return pd_new(multiforA_class);
}
void multiforA_setup(void) {
post("%s", __FUNCTION__);
multiforA_class = class_new(gensym("multiforA"), multiforA_new, 0, sizeof(t_object), 0, A_NULL);
class_addfloat(multiforA_class, multiforA_float);
}

View file

@ -0,0 +1,13 @@
#include <m_pd.h>
t_class*multiforB_class;
static void multiforB_float(t_object*x, t_float f1) {
pd_error(x, "%s got %f", __FUNCTION__, f1);
}
static void*multiforB_new(void) {
return pd_new(multiforB_class);
}
void multiforB_setup(void) {
post("%s", __FUNCTION__);
multiforB_class = class_new(gensym("multiforB"), multiforB_new, 0, sizeof(t_object), 0, A_NULL);
class_addfloat(multiforB_class, multiforB_float);
}

View file

@ -0,0 +1,32 @@
# Makefile to build class 'multilib' for Pure Data.
# Needs Makefile.pdlibbuilder as helper makefile for platform-dependent build
# settings and rules.
# library name
lib.name = multilib
make-lib-executable=yes
# input source file (class name == source file basename)
class.sources = multilibA.c multilibB.c
# glue for building a multi-object library
lib.setup.sources = $(lib.name).c
# all extra files to be included in binary distribution of the library
datafiles = multilib-help.pd multilib-meta.pd
# include Makefile.pdlibbuilder
# (for real-world projects see the "Project Management" section
# in tips-tricks.md)
PDLIBBUILDER_DIR=../..
include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder
# simplistic tests whether all expected files have been produced/installed
buildcheck: all
test -e multilib.$(extension)
installcheck: install
test -e $(installpath)/multilib.$(extension)
test -e $(installpath)/multilib-help.pd
test -e $(installpath)/multilib-meta.pd

View file

@ -0,0 +1,8 @@
multilib
========
minimal pd-lib-builder project that shows how to compile
a library that contains multiple C-files that are compiled into
a single binary containing different Pd-objectclasses.
this is the general case of the single-binary library structure.

View file

@ -0,0 +1,9 @@
#N canvas 335 160 450 300 12;
#X declare -lib multilib;
#X msg 143 93 7;
#X obj 143 125 multilibA;
#X obj 223 125 multilibB;
#X msg 223 93 12;
#X obj 136 47 declare -lib multilib;
#X connect 0 0 1 0;
#X connect 3 0 2 0;

View file

@ -0,0 +1,9 @@
#N canvas 966 322 200 200 10;
#N canvas 19 51 420 300 META 0;
#X text 10 10 META this is a prototype of a libdir meta file;
#X text 10 51 AUTHOR IOhannes m zmolnig;
#X text 10 110 VERSION 1.0.0;
#X text 10 90 LICENSE CC0;
#X text 10 70 DESCRIPTION Example "multiple" external.;
#X text 10 30 NAME multiple;
#X restore 10 10 pd META;

View file

@ -0,0 +1,8 @@
void multilibA_setup(void);
void multilibB_setup(void);
void multilib_setup(void) {
multilibA_setup();
multilibB_setup();
}

View file

@ -0,0 +1,13 @@
#include <m_pd.h>
t_class*multilibA_class;
static void multilibA_float(t_object*x, t_float f1) {
pd_error(x, "%s got %f", __FUNCTION__, f1);
}
static void*multilibA_new(void) {
return pd_new(multilibA_class);
}
void multilibA_setup(void) {
post("%s", __FUNCTION__);
multilibA_class = class_new(gensym("multilibA"), multilibA_new, 0, sizeof(t_object), 0, A_NULL);
class_addfloat(multilibA_class, multilibA_float);
}

View file

@ -0,0 +1,13 @@
#include <m_pd.h>
t_class*multilibB_class;
static void multilibB_float(t_object*x, t_float f1) {
pd_error(x, "%s got %f", __FUNCTION__, f1);
}
static void*multilibB_new(void) {
return pd_new(multilibB_class);
}
void multilibB_setup(void) {
post("%s", __FUNCTION__);
multilibB_class = class_new(gensym("multilibB"), multilibB_new, 0, sizeof(t_object), 0, A_NULL);
class_addfloat(multilibB_class, multilibB_float);
}

View file

@ -0,0 +1,28 @@
# Makefile to build class 'multiple' for Pure Data.
# Needs Makefile.pdlibbuilder as helper makefile for platform-dependent build
# settings and rules.
# library name
lib.name = multiple
# input source file (class name == source file basename)
class.sources = multipleA.c multipleB.c
# all extra files to be included in binary distribution of the library
datafiles = multiple-help.pd multiple-meta.pd
# include Makefile.pdlibbuilder
# (for real-world projects see the "Project Management" section
# in tips-tricks.md)
PDLIBBUILDER_DIR=../..
include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder
# simplistic tests whether all expected files have been produced/installed
buildcheck: all
test -e multipleA.$(extension)
test -e multipleB.$(extension)
installcheck: install
test -e $(installpath)/multipleA.$(extension)
test -e $(installpath)/multipleB.$(extension)
test -e $(installpath)/multiple-help.pd
test -e $(installpath)/multiple-meta.pd

View file

@ -0,0 +1,8 @@
multiple
========
minimal pd-lib-builder project that shows how to compile
a library that contains multiple C-files that are compiled into
multiple binaries each containing a different Pd-objectclass.
this is the general case of the one-object-per-binary library structure.

View file

@ -0,0 +1,7 @@
#N canvas 335 160 450 300 12;
#X msg 143 93 7;
#X obj 143 125 multipleA;
#X obj 223 125 multipleB;
#X msg 223 93 12;
#X connect 0 0 1 0;
#X connect 3 0 2 0;

View file

@ -0,0 +1,9 @@
#N canvas 966 322 200 200 10;
#N canvas 19 51 420 300 META 0;
#X text 10 10 META this is a prototype of a libdir meta file;
#X text 10 51 AUTHOR IOhannes m zmolnig;
#X text 10 110 VERSION 1.0.0;
#X text 10 90 LICENSE CC0;
#X text 10 70 DESCRIPTION Example "multiple" external.;
#X text 10 30 NAME multiple;
#X restore 10 10 pd META;

View file

@ -0,0 +1,13 @@
#include <m_pd.h>
t_class*multipleA_class;
static void multipleA_float(t_object*x, t_float f1) {
pd_error(x, "%s got %f", __FUNCTION__, f1);
}
static void*multipleA_new(void) {
return pd_new(multipleA_class);
}
void multipleA_setup(void) {
post("%s", __FUNCTION__);
multipleA_class = class_new(gensym("multipleA"), multipleA_new, 0, sizeof(t_object), 0, A_NULL);
class_addfloat(multipleA_class, multipleA_float);
}

View file

@ -0,0 +1,13 @@
#include <m_pd.h>
t_class*multipleB_class;
static void multipleB_float(t_object*x, t_float f1) {
pd_error(x, "%s got %f", __FUNCTION__, f1);
}
static void*multipleB_new(void) {
return pd_new(multipleB_class);
}
void multipleB_setup(void) {
post("%s", __FUNCTION__);
multipleB_class = class_new(gensym("multipleB"), multipleB_new, 0, sizeof(t_object), 0, A_NULL);
class_addfloat(multipleB_class, multipleB_float);
}

View file

@ -0,0 +1,28 @@
# Makefile to build class 'multiplexx' for Pure Data.
# Needs Makefile.pdlibbuilder as helper makefile for platform-dependent build
# settings and rules.
# library name
lib.name = multiplexx
# input source file (class name == source file basename)
class.sources = multiplexxA.cpp multiplexxB.c
# all extra files to be included in binary distribution of the library
datafiles = multiplexx-help.pd multiplexx-meta.pd
# include Makefile.pdlibbuilder
# (for real-world projects see the "Project Management" section
# in tips-tricks.md)
PDLIBBUILDER_DIR=../..
include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder
# simplistic tests whether all expected files have been produced/installed
buildcheck: all
test -e multiplexxA.$(extension)
test -e multiplexxB.$(extension)
installcheck: install
test -e $(installpath)/multiplexxA.$(extension)
test -e $(installpath)/multiplexxB.$(extension)
test -e $(installpath)/multiplexx-help.pd
test -e $(installpath)/multiplexx-meta.pd

View file

@ -0,0 +1,8 @@
multiplexx
========
minimal pd-lib-builder project that shows how to compile
a library that contains multiplexx C-files that are compiled into
multiplexx binaries each containing a different Pd-objectclass.
this is the general case of the one-object-per-binary library structure.

View file

@ -0,0 +1,7 @@
#N canvas 335 160 450 300 12;
#X msg 143 93 7;
#X obj 143 125 multiplexxA;
#X obj 223 125 multiplexxB;
#X msg 223 93 12;
#X connect 0 0 1 0;
#X connect 3 0 2 0;

View file

@ -0,0 +1,9 @@
#N canvas 966 322 200 200 10;
#N canvas 19 51 420 300 META 0;
#X text 10 10 META this is a prototype of a libdir meta file;
#X text 10 51 AUTHOR IOhannes m zmolnig;
#X text 10 110 VERSION 1.0.0;
#X text 10 90 LICENSE CC0;
#X text 10 70 DESCRIPTION Example "multiplexx" external.;
#X text 10 30 NAME multiplexx;
#X restore 10 10 pd META;

View file

@ -0,0 +1,19 @@
#include <m_pd.h>
#include <iostream>
t_class*multiplexxA_class;
static void multiplexxA_float(t_object*x, t_float f1) {
pd_error(x, "%s got %f", __FUNCTION__, f1);
}
static void*multiplexxA_new(void) {
return pd_new(multiplexxA_class);
}
#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus)
extern "C" {
void multiplexxA_setup(void);
}
#endif
void multiplexxA_setup(void) {
std::cerr << __FUNCTION__ << std::endl;
multiplexxA_class = class_new(gensym("multiplexxA"), multiplexxA_new, 0, sizeof(t_object), 0, A_NULL);
class_addfloat(multiplexxA_class, multiplexxA_float);
}

View file

@ -0,0 +1,13 @@
#include <m_pd.h>
t_class*multiplexxB_class;
static void multiplexxB_float(t_object*x, t_float f1) {
pd_error(x, "%s got %f", __FUNCTION__, f1);
}
static void*multiplexxB_new(void) {
return pd_new(multiplexxB_class);
}
void multiplexxB_setup(void) {
post("%s", __FUNCTION__);
multiplexxB_class = class_new(gensym("multiplexxB"), multiplexxB_new, 0, sizeof(t_object), 0, A_NULL);
class_addfloat(multiplexxB_class, multiplexxB_float);
}

View file

@ -0,0 +1,41 @@
# Makefile to build class 'multishared' for Pure Data.
# Needs Makefile.pdlibbuilder as helper makefile for platform-dependent build
# settings and rules.
# library name
lib.name = multishared
# common functions
shared.sources = shared.c
# input source file (class name == source file basename)
class.sources = multisharedA.c multisharedB.c
# all extra files to be included in binary distribution of the library
datafiles = multishared-help.pd multishared-meta.pd
# include Makefile.pdlibbuilder
# (for real-world projects see the "Project Management" section
# in tips-tricks.md)
PDLIBBUILDER_DIR=../..
include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder
# simplistic tests whether all expected files have been produced/installed
buildcheck: all
ifeq ($(shared.extension), $(extension))
test -e lib$(lib.name).$(shared.extension)
else
test -e lib$(lib.name).$(extension).$(shared.extension)
endif
test -e multisharedA.$(extension)
test -e multisharedB.$(extension)
installcheck: install
ifeq ($(shared.extension), $(extension))
test -e $(installpath)/lib$(lib.name).$(shared.extension)
else
test -e $(installpath)/lib$(lib.name).$(extension).$(shared.extension)
endif
test -e $(installpath)/multisharedA.$(extension)
test -e $(installpath)/multisharedB.$(extension)
test -e $(installpath)/multishared-help.pd
test -e $(installpath)/multishared-meta.pd

View file

@ -0,0 +1,9 @@
multishared
===========
minimal pd-lib-builder project that shows how to compile
a library that contains multiple C-files that are compiled into
multiple binaries each containing a different Pd-objectclass.
a local shared library is used for common components.
this is an extended case of the one-object-per-binary library structure.

View file

@ -0,0 +1,7 @@
#N canvas 335 160 450 300 12;
#X msg 143 93 7;
#X obj 143 125 multisharedA;
#X obj 223 125 multisharedB;
#X msg 223 93 12;
#X connect 0 0 1 0;
#X connect 3 0 2 0;

View file

@ -0,0 +1,9 @@
#N canvas 966 322 200 200 10;
#N canvas 19 51 420 300 META 0;
#X text 10 10 META this is a prototype of a libdir meta file;
#X text 10 51 AUTHOR IOhannes m zmolnig;
#X text 10 110 VERSION 1.0.0;
#X text 10 90 LICENSE CC0;
#X text 10 70 DESCRIPTION Example "multishared" external.;
#X text 10 30 NAME multishared;
#X restore 10 10 pd META;

View file

@ -0,0 +1,3 @@
#include <m_pd.h>
void multishared_foo(t_float f);

View file

@ -0,0 +1,14 @@
#include "multishared.h"
t_class*multisharedA_class;
static void multisharedA_float(t_object*x, t_float f1) {
pd_error(x, "%s got %f", __FUNCTION__, f1);
multishared_foo(f1);
}
static void*multisharedA_new(void) {
return pd_new(multisharedA_class);
}
void multisharedA_setup(void) {
post("%s", __FUNCTION__);
multisharedA_class = class_new(gensym("multisharedA"), multisharedA_new, 0, sizeof(t_object), 0, A_NULL);
class_addfloat(multisharedA_class, multisharedA_float);
}

View file

@ -0,0 +1,14 @@
#include "multishared.h"
t_class*multisharedB_class;
static void multisharedB_float(t_object*x, t_float f1) {
pd_error(x, "%s got %f", __FUNCTION__, f1);
multishared_foo(f1);
}
static void*multisharedB_new(void) {
return pd_new(multisharedB_class);
}
void multisharedB_setup(void) {
post("%s", __FUNCTION__);
multisharedB_class = class_new(gensym("multisharedB"), multisharedB_new, 0, sizeof(t_object), 0, A_NULL);
class_addfloat(multisharedB_class, multisharedB_float);
}

View file

@ -0,0 +1,5 @@
#include "multishared.h"
void multishared_foo(t_float f) {
post("%s(%f)", __FUNCTION__, f);
}

View file

@ -0,0 +1,24 @@
# Makefile to build class 'single' for Pure Data.
# Needs Makefile.pdlibbuilder as helper makefile for platform-dependent build
# settings and rules.
# library name
lib.name = single
# input source file (class name == source file basename)
class.sources = single.c
# all extra files to be included in binary distribution of the library
datafiles = single-help.pd single-meta.pd
# include Makefile.pdlibbuilder
# (for real-world projects see the "Project Management" section
# in tips-tricks.md)
PDLIBBUILDER_DIR=../..
include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder
# simplistic tests whether all expected files have been produced/installed
buildcheck: all
test -e single.$(extension)
installcheck: install
test -e $(installpath)/single.$(extension)

View file

@ -0,0 +1,8 @@
single
======
minimal pd-lib-builder project that shows how to compile
a library that contains a single C-file that is compiled into
a single binary containing a single Pd-objectclass.
this is a degenerate case of the one-object-per-binary library structure.

View file

@ -0,0 +1,4 @@
#N canvas 335 160 450 300 12;
#X obj 143 125 single;
#X msg 143 93 7;
#X connect 1 0 0 0;

View file

@ -0,0 +1,9 @@
#N canvas 966 322 200 200 10;
#N canvas 19 51 420 300 META 0;
#X text 10 10 META this is a prototype of a libdir meta file;
#X text 10 51 AUTHOR IOhannes m zmolnig;
#X text 10 110 VERSION 1.0.0;
#X text 10 90 LICENSE CC0;
#X text 10 70 DESCRIPTION Example "single" external.;
#X text 10 30 NAME single;
#X restore 10 10 pd META;

View file

@ -0,0 +1,13 @@
#include <m_pd.h>
t_class*single_class;
static void single_float(t_object*x, t_float f1) {
pd_error(x, "%s got %f", __FUNCTION__, f1);
}
static void*single_new(void) {
return pd_new(single_class);
}
void single_setup(void) {
post("%s", __FUNCTION__);
single_class = class_new(gensym("single"), single_new, 0, sizeof(t_object), 0, A_NULL);
class_addfloat(single_class, single_float);
}

View file

@ -0,0 +1,27 @@
# Makefile to build class 'subdir' for Pure Data.
# Needs Makefile.pdlibbuilder as helper makefile for platform-dependent build
# settings and rules.
# library name
lib.name = subdir
# input source file (class name == source file basename)
class.sources = src/subdir.c src/subdir~.c
# all extra files to be included in binary distribution of the library
datafiles = subdir-help.pd subdir-meta.pd
# include Makefile.pdlibbuilder
# (for real-world projects see the "Project Management" section
# in tips-tricks.md)
PDLIBBUILDER_DIR=../..
include $(PDLIBBUILDER_DIR)/Makefile.pdlibbuilder
# simplistic tests whether all expected files have been produced/installed
buildcheck: all
test -e subdir.$(extension)
test -e subdir~.$(extension)
installcheck: install
test -e $(installpath)/subdir.$(extension)
test -e $(installpath)/subdir~.$(extension)

View file

@ -0,0 +1,8 @@
subdir
======
pd-lib-builder project that shows how to compile
a library that contains a single C-file in a separate src/ directory,
that is compiled into a single binary containing a subdir Pd-objectclass.
this is a special case of the one-object-per-binary library structure.

View file

@ -0,0 +1,13 @@
#include <m_pd.h>
t_class*subdir_class;
static void subdir_float(t_object*x, t_float f1) {
pd_error(x, "%s got %f", __FUNCTION__, f1);
}
static void*subdir_new(void) {
return pd_new(subdir_class);
}
void subdir_setup(void) {
post("%s", __FUNCTION__);
subdir_class = class_new(gensym("subdir"), subdir_new, 0, sizeof(t_object), 0, A_NULL);
class_addfloat(subdir_class, subdir_float);
}

View file

@ -0,0 +1,13 @@
#include <m_pd.h>
t_class*subdir_tilde_class;
static void subdir_tilde_float(t_object*x, t_float f1) {
pd_error(x, "%s got %f", __FUNCTION__, f1);
}
static void*subdir_tilde_new(void) {
return pd_new(subdir_tilde_class);
}
void subdir_tilde_setup(void) {
post("%s", __FUNCTION__);
subdir_tilde_class = class_new(gensym("subdir~"), subdir_tilde_new, 0, sizeof(t_object), 0, A_NULL);
class_addfloat(subdir_tilde_class, subdir_tilde_float);
}

View file

@ -0,0 +1,4 @@
#N canvas 335 160 450 300 12;
#X obj 143 125 subdir;
#X msg 143 93 7;
#X connect 1 0 0 0;

View file

@ -0,0 +1,9 @@
#N canvas 966 322 200 200 10;
#N canvas 19 51 420 300 META 0;
#X text 10 10 META this is a prototype of a libdir meta file;
#X text 10 51 AUTHOR IOhannes m zmolnig;
#X text 10 110 VERSION 1.0.0;
#X text 10 90 LICENSE CC0;
#X text 10 70 DESCRIPTION Example "subdir" external.;
#X text 10 30 NAME subdir;
#X restore 10 10 pd META;

View file

@ -0,0 +1,4 @@
#N canvas 335 160 450 300 12;
#X obj 143 125 subdir~;
#X msg 143 93 7;
#X connect 1 0 0 0;

View file

@ -0,0 +1,70 @@
#!/bin/sh
## simple script to open patches via Pd, and check for errors
## - each patch is opened separately
## - if an error is encountered, the Pd-printout is displayed
## (else it is suppressed)
## - if any of the patches encountered an error, the script will
## exit with a non-0 code
if [ "x${PD}" = "x" ]; then
if [ "x${PDBINDIR}" != "x" ]; then
for exe in pd.com pd pd.exe; do
if [ -x "${PDBINDIR}/${exe}" ]; then
PD="${PDBINDIR}/${exe}"
break
fi
done
if [ "x${PD}" = "x" ]; then
echo "WARNING: couldn't find a usable Pd in '${PDBINDIR}'" 1>&2
fi
fi
fi
if [ "x${PD}" = "x" ]; then
PD=pd
fi
echo "using Pd: ${PD}"
failed=0
failed_tests=""
succeeded=0
open1patch() {
logfile=$(mktemp)
local patch=$1
local patchdir=${patch%%/*}
local patchfile=${patch#*/}
patchfile=${patchfile#/}
#echo "INFO: running ${patchfile} in ${patchdir}"
cd "${patchdir}" && \
${PD} -batch -nrt -noprefs -nostdpath -open "${patchfile}" -send "pd quit" \
>"${logfile}" 2>&1
ret=$?
if grep "error: ... couldn't create" "${logfile}" >/dev/null; then
ret=1
fi
if [ "x${ret}" != "x0" ]; then
echo ""
cat "${logfile}"
echo "FAILED[$ret]: ${patch}"
else
echo "SUCCEEDED: ${patch}"
fi
rm "${logfile}"
return $ret
}
for p in "${@}"; do
if (open1patch "${p}"); then
succeeded=$((succeeded+1))
else
failed=$((failed+1))
failed_tests="${failed_tests} ${p}"
fi
done
echo ""
echo "SUCCESS: ${succeeded}"
echo "FAILURE: ${failed}"
test ${failed} -eq 0 || echo "FAILS :${failed_tests}"
test ${failed} -eq 0

View file

@ -3,16 +3,15 @@ pd-lib-builder cheatsheet
# Creating special builds
## cross-compiling on linux x86_64 for other platforms
## Building for non-native platform
Using pd-lib-builder >=0.6.0 we can define variable `PLATFORM` to specify a
target triplet for cross-compilation. Example to build W32 binaries (assuming
package `mingw-w64` is installed and a W32 package for Pd is unzipped into a
path `${PDWIN32}`:
target triplet for cross-compilation. Assuming a W32 package for Pd is unzipped
into path `${PDWIN32}`, to build for Windows 32 bit:
make PLATFORM=x86_64-w64-mingw32 PDDIR="${PDWIN32}"
make PLATFORM=i686-w64-mingw32 PDDIR="${PDWIN32}"
#### older pd-lib-builder versions
#### Older pd-lib-builder versions
Using pd-lib-builder < 0.6.0, in the absence of variable `PLATFORM`, you would
instead override variables `system`, `target.arch`, `CC` and / or `CXX`,
@ -20,44 +19,66 @@ instead override variables `system`, `target.arch`, `CC` and / or `CXX`,
make system=Windows target.arch=i686 CC=i686-w64-mingw32-gcc STRIP=i686-w64-mingw32-strip PDDIR="${PDWIN32}"
#### toolchains
#### Toolchains
Cross toolchains for relevant platforms in Debian Buster (install g++
with dependencies for a given platform to get the whole tool chain):
To build for non-native OS and/or architecture you need a cross toolchain. On
Linux such toolchains are relatively easy to get. For example Debian Buster
amd64 provides them for the following platforms (install g++ with dependencies
for a given platform to get the whole toolchain):
- `arm-linux-gnueabihf`
- `aarch64-linux-gnu`
- `i686-linux-gnu`
- `i686-w64-mingw32` and `x86_64-w64-mingw32` (install `mingw-w64`)
OSX/MacOS cross tool chains are not distributed by Debian. Use project
`osxcross` from Thomas Poechtraeger to create the tools.
Cross toolchains for OSX/MacOS are not generally distributed. Project
`osxcross` from Thomas Poechtraeger can create them for Linux.
## building double-precision externals
## Universal binaries on macOS
At the time of writing (2018-02) there is no official Pd that supports
The compiler, by default, builds for the native architecture of the build
machine. To make a "universal" multi-arch build, specify the desired
archtectures on the command line using the "arch" pd-lib-builder Makefile
variable.
For example, to build a "fat" external for both 64-bit Intel and Arm (Apple
Silicon):
make arch="x86_64 arm64"
If the build is successful, the compiled architectures in the built external can
be confirmed via the `file` command:
~~~sh
% file vbap.pd_darwin
vbap.pd_darwin: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit bundle x86_64] [arm64:Mach-O 64-bit bundle arm64]
vbap.pd_darwin (for architecture x86_64): Mach-O 64-bit bundle x86_64
vbap.pd_darwin (for architecture arm64): Mach-O 64-bit bundle arm64
~~~
Note: The available architectures depend on which macOS version & command line
tools/Xcode combination the build system has. For example, any newer macOS
10.15+ will support both x86_64 (Intel 64-bit) and arm64 (Apple Silicon) while
OSX 10.6 - macOS 10.14 can build for x86_64 and i386 (Intel 32-bit).
## Building double-precision externals
At the time of writing (2023-07-06) there is no official Pd that supports
double-precision numbers yet.
However, if you do get hold of an experimental double-precision Pd, you can
easily build your externals for 64-bit numbers:
easily build your externals for 64-bit numbers, by passing `floatsize=64`
as an argument to `make`.
Starting with Pd>=0.54, double precision externals use different extensions
from traditional (single-precision) externals.
The extension consists of the OS ("linux", "darwin", "windows"), the CPU
architecture ("amd64" (x86_64), "i386" (x86), "arm64",...) and the floatsize
in bits ("64" for double-precision), followed by the system's native extension
for dynamic libraries (".dll" on Windows, ".so" on macOS/Linux/un*xes).
As of pd-lib-builder==0.7.0, you have to manually pass this extension:
make CPPFLAGS="-DPD_FLOATSIZE=64"
## building externals for W64 (64-bit Windows)
At the time of writing (2018-02) there is no official Pd that supports
W64 yet.
However, if you do get hold of an experimental W64 Pd, you can
easily build your externals for this environment with
make CPPFLAGS="-DPD_LONGINTTYPE=__int64" CC=x86_64-w64-mingw32-gcc
To build a double-precision external for W64, use something like:
make CPPFLAGS="-DPD_LONGINTTYPE=__int64 -DPD_FLOATSIZE=64" CC=x86_64-w64-mingw32-gcc
## TODO universal binaries on OSX
make floatsize=64 extension=windows-amd64-64.dll
make floatsize=64 extension=linux-arm64-64.so
make floatsize=64 extension=darwin-fat-64.so arch="x86_64 arm64"
# Project management
@ -97,6 +118,7 @@ In short, `git subtree` is the better `git submodule`.
So here's how to do it:
#### Initial setup/check-out
This will create a `pd-lib-builder/` directory containing the full history of
the pd-lib-builder repository up to its release `v0.5.0`
@ -108,6 +130,7 @@ This will automatically merge the `pd-lib-builder/` history into your current
branch, so everything is ready to go.
#### Cloning your repository with the subtree
Nothing special, really.
Just clone your repository as always:
@ -116,6 +139,7 @@ git clone https://git.example.org/pd/superbonk~.git
~~~
#### Updating the subtree
Time passes and sooner or later you will find, that there is a shiny new
pd-lib-builder with plenty of bugfixes and new features.
To update your local copy to pd-lib-builder's current `master`, simply run:
@ -125,6 +149,7 @@ git subtree pull --prefix pd-lib-builder/ https://github.com/pure-data/pd-lib-bu
~~~
#### Pulling the updated subtree into existing clones
Again, nothing special.
Just pull as always:
@ -132,8 +157,8 @@ Just pull as always:
git pull
~~~
#### Further reading
More on the power of `git subtree` can be found online
- https://medium.com/@v/git-subtrees-a-tutorial-6ff568381844
- https://www.atlassian.com/blog/git/alternatives-to-git-submodule-git-subtree
@ -141,8 +166,8 @@ More on the power of `git subtree` can be found online
### ~~`git submodule`~~ [DISCOURAGED]
#### Initial setup/check-out
To add a new submodule to your repository, just run `git submodule add` and
commit the changes:
@ -169,6 +194,7 @@ git submodule update
~~~
#### Updating the submodule
Submodules are usually fixed to a given commit in their repository.
To update the `pd-lib-builder` submodule to the current `master` do something
like:
@ -183,6 +209,7 @@ git commit pd-lib-builder -m "Updated pd-lib-builder to current master"
~~~
#### Pulling the updated submodule into existing clones
After you have pushed the submodule updates in your repository, other clones of
the repository can be updated as follows:
@ -212,6 +239,7 @@ git submodule update
~~~
#### Drawbacks
`git submodule` has a number of drawbacks:
- it requires special commands to synchronize the submodules, in addition to
synching your repository.
@ -227,4 +255,3 @@ git submodule update
In general, I would suggest to **avoid** `git submodule`, and instead use the
better `git subtree` (above).