More work in progress.
authorMark Wooding <mdw@distorted.org.uk>
Mon, 9 Sep 2019 12:51:56 +0000 (13:51 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Wed, 11 Sep 2019 11:05:56 +0000 (12:05 +0100)
I think it can make native chroots now.  We're most of the way towards
foreign chroots, but the work's not finished yet.

12 files changed:
.gitignore
Makefile
bin/buildwrap [new file with mode: 0755]
bin/install-cross-tools [new file with mode: 0755]
bin/mkbuildchroot
bin/mkchrootconf
bin/update-cross-tools [new file with mode: 0755]
etc/schroot-scripts/11private [new file with mode: 0755]
etc/schroot-scripts/15binfmt [new file with mode: 0755]
etc/schroot-scripts/51chrootenv [new file with mode: 0755]
pkg/checkpath-1.2.4.1.tar.gz [new file with mode: 0644]
pkg/mLib-2.3.3.1.tar.gz [new file with mode: 0644]

index 2706c7e..d9f3627 100644 (file)
@@ -3,4 +3,6 @@
 /etc/aptsrc.local.conf
 /local.mk
 /local.schroot/
+/log/
+/mnt/
 /state/
index dd96989..08906ba 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -26,7 +26,8 @@
 
 all::
 clean::
-.PHONY: all clean
+check::
+.PHONY: all clean check
 .SECONDEXPANSION: #sorry
 
 ###--------------------------------------------------------------------------
@@ -34,14 +35,22 @@ clean::
 
 CONFIG_VARS             =
 
+## Level of pickiness to aspire to.
+NOTIFY_FATAL            = 1
+
+## Path to this working tree.
+CONFIG_VARS            += HERE
+HERE                    = $(abspath .)
+
 ## Volume group from which to allocate chroot volumes and snapshots.
 CONFIG_VARS            += VG LVPREFIX
 VG                      = vg-$(shell hostname)
 LVPREFIX                =
 
 ## Logical volume size, as an LVM option.
-CONFIG_VARS            += LVSZ
+CONFIG_VARS            += LVSZ SNAPOPT
 LVSZ                    = -L8G
+SNAPOPT                         = -L8G
 
 ## Debian mirror.
 CONFIG_VARS            += DEBMIRROR
@@ -88,6 +97,19 @@ FOREIGN_CHROOTS               = $(foreach a,$(FOREIGN_ARCHS), \
                                $(foreach d,$(or $($a_DISTS) $(DISTS)), \
                                        $d-$a))
 
+## Extra packages to be installed in chroots.
+CONFIG_VARS            += BASE_PACKAGES NATIVE_BASE_PACKAGES FOREIGN_BASE_PACKAGES
+BASE_PACKAGES           = ccache
+BASE_PACKAGES          += eatmydata fakeroot
+BASE_PACKAGES          += locales tzdata
+BASE_PACKAGES          += libfile-fcntllock-perl
+NATIVE_BASE_PACKAGES    = build-essential
+FOREIGN_BASE_PACKAGES   =
+
+## Local packages to be compiled and installed in chroots.  Archives can be
+## found in `pkg/'.
+LOCALPKGS               = mLib checkpath
+
 ## Which host architecture to use for foreign architectures.  It turns out
 ## that it's best to use a Qemu with the same host bitness as the target
 ## architecture; otherwise it has to do a difficult job of converting
@@ -142,12 +164,51 @@ SCHROOT_NSSDATABASES       = passwd shadow group gshadow
 -include local.mk
 
 ## All chroot names.
-CONFIG_VARS            += ALL_CHROOTS
+CONFIG_VARS            += ALL_ARCHS ALL_CHROOTS
+ALL_ARCHS               = $(NATIVE_ARCHS) $(FOREIGN_ARCHS)
 ALL_CHROOTS             = $(NATIVE_CHROOTS) $(FOREIGN_CHROOTS)
 
 ###--------------------------------------------------------------------------
 ### Utilities.
 
+## Hack to force rebuilding.
+_force:
+.PHONY: _force
+
+## Hack to not delimit function arguments.  (Ugh!)
+comma                   = ,
+
+## Silent-rules machinery.
+V                       = 0
+V_AT                    = $(V_AT_$V)
+V_AT_0                  = @
+v_print                         = $(call v_print_$V,$1,$2)
+v_print_0               = printf "  %-8s %s\n" "$1" $(call squote,$2);
+v_tag                   = $(V_AT)$(call v_print,$1,$@)
+v_log                   = $(call v_log_$V,$1,$2)
+v_log_                  = $(call v_log_1,$1,$2)
+v_log_0                         = $2 >log/$1.log 2>&1
+v_log_1                         = $(call catchrc,$(call throwrc,$2) 2>&1 | tee log/$1.log)
+v_echo                  = $(call v_echo_$V,$1)
+v_echo_0                = :
+v_echo_1                = printf "%s\n" $1
+CLEANFILES             += log/*.log
+
+## Oh, shut up.
+SILENCE_LVM             = \
+       LVM_SUPPRESS_FD_WARNINGS=1; export LVM_SUPPRESS_FD_WARNINGS
+
+##     $(call catchrc,...$(call throwrc,CMD)...)
+##
+## Catch the exit status of some subpart of a complicated shell rune.
+catchrc                         = (exec 3>&1; exit $$({ $1; } 4>&1 >&3 3>&-))
+throwrc                         = { $1; echo $$? >&4; }
+
+##     $(call squote,TXT)
+##
+## Single-quote TXT.
+squote                  = '$(subst ','\\'',$1)'
+
 ##     $(call chroot-dist,D-A) -> D
 ##     $(call chroot-arch,D-A) -> A
 ##
@@ -155,24 +216,42 @@ ALL_CHROOTS                = $(NATIVE_CHROOTS) $(FOREIGN_CHROOTS)
 chroot-dist             = $(patsubst %/,%,$(dir $(subst -,/,$1)))
 chroot-arch             = $(notdir $(subst -,/,$1))
 
+##     $(call package-dir-name,P-V) -> P
+##     $(call package-dir-version,P-V) -> V
+##
+## Parse (source) package directory names.
+package-dir-name        = $(sort $(foreach p,$(LOCALPKGS),$(if $(filter $p-$($p_VERSION),$1),$p)))
+package-dir-version     = $($(call package-dir-name,$1)_VERSION)
+
+##     $(call package-dir,P-V.A) -> P-V
+##     $(call package-name,P-V.A) -> P
+##     $(call package-version,P-V.A) -> V
+##     $(call package-arch,P-V.A) -> A
+##
+## Parse package stamp names.
+package-dir             = $(basename $1)
+package-name            = $(call package-dir-name,$(call package-dir,$1))
+package-version                 = $(call package-dir-version,$(call package-dir,$1))
+package-arch            = $(patsubst .%,%,$(suffix $1))
+
 ##     $(call native-chroot-p,D-A) -> D | <empty>
 ##
 ## Answer whether D-A is a native chroot.
-native-chroot-p                 = $(filter $(call chroot-arch,$1), $(NATIVE_ARCHS))
+native-chroot-p                 = $(filter $(call chroot-arch,$1),$(NATIVE_ARCHS))
 
 ##     $(call chroot-qemuhost,D-A) -> AA
 ##
 ## Answer the apporopriate Qemu host architecture for foreign chroot D-A.
 chroot-qemuhost                 = $($(call chroot-arch,$1)_QEMUHOST)
 
-##     $(call chroot-deps,PRE,D-A) -> PRE/DD-AA ... | <empty>
+##     $(call chroot-deps,PRE,D-A) -> PREDD-AA ... | <empty>
 ##
 ## Answer a list of additional dependencies for the chroot D-A: specifically,
 ## if D-A is foreign then include PRE/DD-AA entries for the tools
 ## architecture, and Qemu host architecture (if that's different).
 chroot-deps             = $(if $(call native-chroot-p,$2),, \
-                               $(addprefix $1/$(call chroot-dist,$2)-,\
-                                       $(sort $(call chroot-toolsarch,$2) \
+                               $(addprefix $1$(call chroot-dist,$2)-,\
+                                       $(sort $(TOOLSARCH) \
                                               $(call chroot-qemuhost,$2))))
 
 ## Substituting placeholders in files.
@@ -188,18 +267,73 @@ subst-file                 = { \
        sed "$$substs"; \
 }
 
-## Silent-rules machinery.
-V                       = 0
-V_AT                    = $(V_AT_$V)
-V_AT_0                  = @
-v_print                         = $(call v_print_$V,$1,$2)
-v_print_0               = printf "  %-8s %s\n" "$1" "$2";
-v_tag                   = $(V_AT)$(call v_print,$1,$@)
-v_log                   = $(call v_log_$V,$1)
-v_log_                  = $(call v_log_1,$1)
-v_log_0                         = >$1.log 2>&1
-v_log_1                         = 2>&1 | tee $1.log
-CLEANFILES             += *.log
+###    $(call symlink-ok-p,LINK,DEST)
+###
+### Expand to `t' if LINK is a symbolic link to DEST, and empty otherwise.
+symlink-ok-p             = $(shell \
+       case $$(readlink 2>/dev/null $(patsubst %/,%,$1)) in ($2) echo t ;; esac)
+
+###    $(call general-notify,SEV,COLOUR,PREFIX,MSG)
+###
+### Report a message, highlighted in the right way, and maybe fail
+general-notify          = { \
+       echo "$$(tput bold; tput setaf $2)$3 "$4"$$(tput sgr0; tput op)"; \
+       if [ "$1" -le "$(NOTIFY_FATAL)" ]; then exit 2; fi; \
+}
+
+###    $(call report/SEV,MSG)
+###
+### Report a notification of a particular severity.
+notify/INFO             = $(call general-notify,3,6,---,$1)
+notify/WARN             = $(call general-notify,2,5,???,$1)
+notify/ERR              = $(call general-notify,1,1,!!!,$1)
+
+##     $(call check,SEV,MSG,UNLESS)
+##
+## If UNLESS completes successfully, all is OK; otherwise print MSG to stderr
+## and fail.
+check                   = @{ \
+       $(call v_echo,'check: '$(call squote,$3)''); \
+       if ! { $3; }; then $(call notify/$1,$2); fi; \
+}
+
+##     $(call check-executable,SEV,PATH)
+##
+## Verify that PATH is an executable program.
+check-executable        = $(call check,$1,"\`$2' is not an executable", \
+       [ -x "$2" ])
+
+##     $(call check-mountpoint,SEV,DIR)
+##
+## Verify that DIR is a mountpoint.
+check-mountpoint        = $(call check,$1,"\`$2' is not a mount point", \
+       mountpoint -q "$2")
+
+##     $(call check-symlink,SEV,LINK,DEST)
+##
+## Verify that LINK is a symbolic link pointing to DEST.
+check-symlink           = $(call check,$1,"\`$2' is not a link to \`$3'", \
+       [ -L "$2" ] && [ "$$(readlink "$2")" = "$3" ])
+
+###--------------------------------------------------------------------------
+### Scripts.
+
+SCRIPTS                        += mkbuildchroot
+SCRIPTS                        += mkchrootconf
+SCRIPTS                        += install-cross-tools update-cross-tools
+
+SUBST_SCRIPTS           = $(addprefix $(STATE)/bin/,$(SCRIPTS))
+all:: $(SUBST_SCRIPTS)
+$(SUBST_SCRIPTS): $(STATE)/bin/%: bin/% $(STATE)/config.sh
+       $(V_AT)mkdir -p $(dir $@)
+       $(call v_tag,SUBST){ \
+               sed \
+               -e '2i### GENERATED by distorted-chroot: do not edit' \
+               -e '/@@@config@@@/ {' \
+                       -e 'r $(STATE)/config.sh' \
+                       -e 'd'\
+               -e '}' $<; \
+       } >$@.new && chmod +x $@.new && mv $@.new $@
 
 ###--------------------------------------------------------------------------
 ### APT configuration.
@@ -233,6 +367,29 @@ $(APT_CONFIGS): $(LOCAL)/etc/apt/apt.conf.d/%: \
                $$(or $$($$*_APTCONFSRC) $$(APTCONF_DIR)/$$*)
        $(V_AT)mkdir -p $(dir $@)
        $(call v_tag,COPY)cp $< $@.new && mv $@.new $@
+clean::; rm -f $(APT_CONFIGS)
+
+###--------------------------------------------------------------------------
+### Build hacks.
+
+check::; $(call check-executable,ERR,/usr/bin/eatmydata)
+
+EATMYDATA_HACKS                += apt-get aptitude dpkg
+SYMLINK_EATMYDATA_HACKS         = $(addprefix $(LOCAL)/hacks/,$(EATMYDATA_HACKS))
+all:: $(SYMLINK_EATMYDATA_HACKS)
+$(SYMLINK_EATMYDATA_HACKS): $(LOCAL)/hacks/%: \
+               $$(if $$(call symlink-ok-p,$$@,/usr/bin/eatmydata),,_force)
+       $(V_AT)mkdir -p $(dir $@)
+       $(call v_tag,SYMLINK)ln -sf /usr/bin/eatmydata $@.new && mv $@.new $@
+clean::; rm -f $(SYMLINK_EATMYDATA_HACKS)
+
+SCRIPT_HACKS           += buildwrap
+COPY_SCRIPT_HACKS       = $(addprefix $(LOCAL)/hacks/,$(SCRIPT_HACKS))
+all:: $(COPY_SCRIPT_HACKS)
+$(COPY_SCRIPT_HACKS): $(LOCAL)/hacks/%: bin/%
+       $(V_AT)mkdir -p $(dir $@)
+       $(call v_tag,COPY)cp $< $@.new && mv $@.new $@
+clean::; rm -f $(COPY_SCRIPT_HACKS)
 
 ###--------------------------------------------------------------------------
 ### `schroot' and `sbuild' configuration.
@@ -241,6 +398,10 @@ all:: schroot-config
 schroot-config::
 .PHONY: schroot-config
 
+check::; $(call check-mountpoint,WARN,/var/lib/sbuild/build)
+check::; $(call check-symlink,WARN,/build,/var/lib/sbuild/build)
+check::; $(call check-symlink,ERR,/schroot,/run/schroot/mount)
+
 %print-varlist          = { \
        echo "\#\#\# -*-sh-*- GENERATED by distorted-chroot: do not edit"; \
        $(foreach v,$1, echo $v=\''$(subst ','\'\\\'\'',$($v))'\';) \
@@ -260,9 +421,11 @@ $(STATE)/config.sh: $(schroot-config_FILE)
        $(call v_tag,SYMLINK)ln -sf $(notdir $<) $@
 
 schroot-config:: $(STATE)/etc/schroot/sbuild.schroot
-$(STATE)/etc/schroot/sbuild.schroot: $(STATE)/config.sh bin/mkchrootconf
+$(STATE)/etc/schroot/sbuild.schroot: $(STATE)/bin/mkchrootconf
        $(V_AT)mkdir -p $(dir $@)
-       $(call v_tag,GEN)bin/mkchrootconf >$@.new && mv $@.new $@
+       $(call v_tag,GEN)$(STATE)/bin/mkchrootconf >$@.new && \
+               $(ROOTLY) chown root:root $@.new && mv $@.new $@
+check::; $(call check-symlink,WARN,/etc/schroot/chroot.d/sbuild,$(HERE)/$(STATE)/etc/schroot/sbuild.schroot)
 
 schroot-config:: $(STATE)/etc/schroot/sbuild.profile/copyfiles
 $(STATE)/etc/schroot/sbuild.profile/copyfiles: $(schroot-config_STAMP)
@@ -287,31 +450,199 @@ $(STATE)/etc/schroot/sbuild.profile/fstab: \
        $(call v_tag,SUBST)$(call subst-file,### -*-conf-*-) \
                <$< >$@.new && mv $@.new $@
 
+check::; $(call check-symlink,WARN,/etc/schroot/sbuild,$(HERE)/$(STATE)/etc/schroot/sbuild.profile)
+
 schroot-config:: $(STATE)/etc/sbuild.conf
 $(STATE)/etc/sbuild.conf: etc/sbuild.conf.in $(schroot-config_STAMP)
        $(V_AT)mkdir -p $(dir $@)
        $(call v_tag,SUBST)$(call subst-file,### -*-perl-*-) \
                <$< >$@.new && mv $@.new $@
 
+SCHROOT_SCRIPTS                += 11private
+SCHROOT_SCRIPTS                += 15binfmt
+SCHROOT_SCRIPTS                += 51chrootenv
+COPY_SCHROOT_SCRIPTS    = $(addprefix $(STATE)/etc/schroot/setup.d/,$(SCHROOT_SCRIPTS))
+schroot-config:: $(COPY_SCHROOT_SCRIPTS)
+$(COPY_SCHROOT_SCRIPTS): \
+               $(STATE)/etc/schroot/setup.d/%: etc/schroot-scripts/%
+       $(V_AT)mkdir -p $(dir $@)
+       $(call v_tag,COPY)cp $< $@.new && mv $@.new $@
+
+CHECK_SCHROOT_SCRIPTS   = $(addprefix check-script/,$(SCHROOT_SCRIPTS))
+check:: $(CHECK_SCHROOT_SCRIPTS)
+$(CHECK_SCHROOT_SCRIPTS): check-script/%:
+       $(call check-symlink,WARN,/etc/schroot/setup.d/$*,$(HERE)/$(STATE)/etc/schroot/setup.d/$*)
+.PHONY: $(addprefix check-script/,$(SCHROOT_SCRIPTS))
+
+###--------------------------------------------------------------------------
+### `/usr/local/' structure.
+
+LOCAL_COMMON_DIRS       = share/ src/
+all:: $(foreach d,$(LOCAL_COMMON_DIRS),$(LOCAL)/$d)
+$(foreach d,$(LOCAL_COMMON_DIRS),$(LOCAL)/$d):
+       $(V_AT)mkdir -p $(dir $(patsubst %/,%,$@))
+       $(call v_tag,MKDIR)mkdir $@
+
+LOCAL_ARCH_DIRS                 = bin/ etc/ games/ include/ lib/ libexec/ sbin/
+LOCAL_ARCH_LINKS        = man
+man_LINKDEST            = share/man
+all:: $(foreach a,$(ALL_ARCHS),\
+       $(LOCAL)/$a/ \
+       $(foreach d,$(LOCAL_ARCH_DIRS),$(LOCAL)/$a/$d) \
+       $(foreach d,$(LOCAL_ARCH_LINKS),$(LOCAL)/$a/$d) \
+       $(foreach d,$(LOCAL_COMMON_DIRS),$(LOCAL)/$a/$d))
+
+$(foreach a,$(ALL_ARCHS),$(LOCAL)/$a/):
+       $(call v_tag,MKDIR)mkdir $@
+$(foreach a,$(ALL_ARCHS),\
+  $(foreach d,$(LOCAL_ARCH_DIRS),$(LOCAL)/$a/$d)):
+       $(V_AT)mkdir -p $(dir $(patsubst %/,%,$@))
+       $(call v_tag,MKDIR)mkdir $@
+$(foreach a,$(ALL_ARCHS),\
+  $(foreach d,$(LOCAL_ARCH_LINKS),$(LOCAL)/$a/$d)): \
+               $$(if $$(call symlink-ok-p,$$@,$$($$(notdir $$@)_LINKDEST)),,_force)
+       $(V_AT)mkdir -p $(notdir $@)
+       $(call v_tag,SYMLINK)ln -sf $($(notdir $@)_LINKDEST) $@
+$(foreach a,$(ALL_ARCHS),\
+  $(foreach d,$(LOCAL_COMMON_DIRS),$(LOCAL)/$a/$d)): \
+               $$(if $$(call symlink-ok-p,$$@,../$$(notdir $$(patsubst %/,%,$$@))),,_force)
+       $(V_AT)mkdir -p $(dir $(patsubst %/,%,$@))
+       $(call v_tag,SYMLINK)ln -sf ../$(notdir $(patsubst %/,%,$@)) $(patsubst %/,%,$@)
+
 ###--------------------------------------------------------------------------
 ### Constructing chroots.
 
+chroot-stamp            = $(addprefix $(STATE)/stamp/chroot.,$1)
 BUILD_CHROOTS           = $(addprefix chroot/,$(ALL_CHROOTS))
-CHROOT_STAMPS           = $(addprefix $(STATE)/stamp/chroot.,$(ALL_CHROOTS))
-all:: setup-chroots
+CHROOT_STAMPS           = $(call chroot-stamp,$(ALL_CHROOTS))
 setup-chroots: $(BUILD_CHROOTS)
 $(BUILD_CHROOTS): chroot/%: $(STATE)/stamp/chroot.%
 .PHONY: setup-chroots $(BUILD_CHROOTS)
 
-$(CHROOT_STAMPS): $(STATE)/stamp/chroot.%: $(STATE)/config.sh bin/mkbuildchroot
-       $(call v_tag,CHROOT)bin/mkbuildchroot \
-               $(call chroot-dist,$*) $(call chroot-arch,$*) \
-               $(call v_log,setup-chroot.$*)
+$(CHROOT_STAMPS): $(STATE)/stamp/chroot.%: \
+               $$(call chroot-deps,$(STATE)/stamp/cross-tools.,$$*)
+       $(V_AT)mkdir -p $(dir $@) log/
+       $(MAKE) \
+               $(STATE)/bin/mkbuildchroot $(STATE)/bin/install-cross-tools \
+               $(STATE)/etc/schroot/sbuild.schroot
+       $(call v_tag,CHROOT)$(call v_log,setup-chroot.$*, \
+               $(SILENCE_LVM); \
+               $(ROOTLY) $(STATE)/bin/mkbuildchroot \
+                       $(call chroot-dist,$*) $(call chroot-arch,$*))
+       $(V_AT)touch $@
+
+UPDATE_CHROOTS          = $(addprefix update/,$(ALL_CHROOTS))
+update-chroots: $(UPDATE_CHROOTS)
+$(UPDATE_CHROOTS): update/%: $(STATE)/stamp/chroot.%
+       $(V_AT)mkdir -p log/
+       $(MAKE) $(STATE)/bin/install-cross-tools
+       $(call v_tag,UPDATE)$(call v_log,update-chroot.$*, { \
+               schroot -uroot -csource:$(LVPREFIX)$* -- \
+                       apt-get update && \
+               schroot -uroot -csource:$(LVPREFIX)$* -- \
+                       apt-get -y dist-upgrade && \
+               schroot -uroot -csource:$(LVPREFIX)$* -- \
+                       apt-get -y autoremove && \
+               schroot -uroot -csource:$(LVPREFIX)$* -- \
+                       apt-get -y clean && \
+               $(if $(filter $*,$(FOREIGN_CHROOTS)), \
+                       $(ROOTLY) $(STATE)/bin/install-cross-tools \
+                               $(call chroot-dist,$*) \
+                               $(call chroot-arch,$*), \
+                       :); \
+       })
+.PHONY: update-chroots $(UPDATE_CHROOTS)
+
+cross-tools-stamp       = $(addprefix $(STATE)/stamp/cross-tools.,$1)
+CROSS_TOOLS             = $(addprefix cross-tools/,$(NATIVE_CHROOTS))
+UPDATE_CROSS_TOOLS      = $(addprefix update-cross-tools/,$(NATIVE_CHROOTS))
+cross-tools: $(CROSS_TOOLS)
+update-cross-tools: $(UPDATE_CROSS_TOOLS)
+$(CROSS_TOOLS): cross-tools/%: $(STATE)/stamp/cross-tools.%
+define updcross
+       $(V_AT)mkdir -p log/
+       $(MAKE) $(STATE)/bin/update-cross-tools
+       $(call v_tag,UPDCROSS)$(call v_log,update-cross-tools.$*, \
+               $(STATE)/bin/update-cross-tools \
+                       $(call chroot-dist,$*) \
+                       $(call chroot-arch,$*))
+       $(V_AT)touch $(call cross-tools-stamp,$*)
+endef
+$(call cross-tools-stamp,$(NATIVE_CHROOTS)): $(STATE)/stamp/cross-tools.%: \
+               $$(call chroot-stamp,$$*)
+       $(V_AT)mkdir -p $(dir $@)
+       $(updcross)
+$(UPDATE_CROSS_TOOLS): update-cross-tools/%: \
+               $$(call chroot-stamp,$$*) _force
+       $(updcross)
+.PHONY: cross-tools update-cross-tools $(CROSS_TOOLS) $(UPDATE_CROSS_TOOLS)
 
 ###--------------------------------------------------------------------------
 ### Installing basic custom software.
 
-
+$(foreach p,$(LOCALPKGS), $(eval $p_VERSION := $(shell \
+       set -- pkg/$p-[0-9]*.tar.gz; \
+       case $$#,$$1 in \
+         (1,*\**) echo "NOT-FOUND"; exit 2 ;; \
+         (1,*) v=$${1#pkg/$p-}; v=$${v%.tar.gz}; echo "$$v" ;; \
+         (*) echo "AMBIGUOUS"; exit 2 ;; \
+       esac)))
+
+pkg-stamp               = \
+       $(foreach p,$1,$(STATE)/stamp/package.$p-$($p_VERSION).$2)
+unpack-pkg-stamp        = \
+       $(foreach p,$1,$(STATE)/stamp/unpack.$p-$($p_VERSION))
+PACKAGE_STAMPS          = \
+       $(foreach a,$(ALL_ARCHS),$(call pkg-stamp,$(LOCALPKGS),$a))
+INSTALL_PACKAGES        = $(addprefix install/,$(LOCALPKGS))
+install-packages: $(INSTALL_PACKAGES)
+$(INSTALL_PACKAGES): install/%: \
+       $$(foreach a,$$(ALL_ARCHS),$$(call pkg-stamp,$$*,$$a))
+
+$(foreach p,$(LOCALPKGS),$(call unpack-pkg-stamp,$p)): \
+               $(STATE)/stamp/unpack.%: pkg/%.tar.gz
+       $(V_AT)mkdir -p $(dir $@) $(LOCAL)/src/
+       $(call v_tag,UNPACK){ \
+               set -e; \
+               p=$(call package-dir-name,$*); \
+               v=$(call package-dir-version,$*); \
+               cd $(LOCAL)/src/; \
+               $(ROOTLY) rm -rf $$p-*; \
+               mkdir $$p-$$v.unpack; \
+               (cd $$p-$$v.unpack && tar xf $(HERE)/$<); \
+               mv $$p-$$v.unpack/$$p-$$v $$p-$$v; \
+               rmdir $$p-$$v.unpack/; \
+               cd $(HERE); \
+               touch $@; \
+       }
+
+$(PACKAGE_STAMPS): $(STATE)/stamp/package.%: \
+               $$(call unpack-pkg-stamp,$$(call package-name,$$*)) \
+               $$(call chroot-stamp,$$(PRIMARY_DIST)-$$(call package-arch,$$*))
+       $(V_AT)mkdir -p $(dir $@) log/
+       $(call v_tag,BUILD)$(call v_log,install-package.$*, { \
+               $(SILENCE_LVM); \
+               schroot -uroot -c$(LVPREFIX)$(PRIMARY_DIST)-$(call package-arch,$*) -- \
+               sh -exc ' \
+                       mount -oremount$(comma)rw /usr/local.schroot; \
+                       eatmydata apt-get update; \
+                       eatmydata apt-get -y install pkg-config; \
+                       p=$(call package-name,$*); \
+                       v=$(call package-version,$*); \
+                       a=$(call package-arch,$*); \
+                       cd /usr/local/src/$$p-$$v/; \
+                       rm -rf build.$$a/; \
+                       mkdir build.$$a/; \
+                       cd build.$$a/; \
+                       ../configure PKG_CONFIG_PATH=/usr/local/lib/pkgconfig.hidden; \
+                       make -j4; \
+                       make install; \
+                       mkdir -p /usr/local/lib/pkgconfig.hidden; \
+                       mv /usr/local/lib/pkgconfig/*.pc /usr/local/lib/pkgconfig.hidden || :' && \
+               schroot -uroot -csource:$(LVPREFIX)$(PRIMARY_DIST)-$(call package-arch,$*) -- \
+                       ldconfig; \
+       })
+       $(V_AT)touch $@
 
 ###--------------------------------------------------------------------------
 ### Other maintenance targets.
diff --git a/bin/buildwrap b/bin/buildwrap
new file mode 100755 (executable)
index 0000000..e367b1b
--- /dev/null
@@ -0,0 +1,60 @@
+#! /bin/sh -e
+###
+### Wrapper around `sbuild' builds
+###
+### (c) 2018 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk chroot maintenance tools.
+###
+### distorted-chroot is free software: you can redistribute it and/or
+### modify it under the terms of the GNU General Public License as
+### published by the Free Software Foundation; either version 2 of the
+### License, or (at your option) any later version.
+###
+### distorted-chroot is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+### General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with distorted-chroot.  If not, write to the Free Software
+### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+### USA.
+
+## Set up compiler caching.  This makes a big difference to build times.
+PATH=/usr/lib/ccache:$PATH; export PATH
+CCACHE_DIR=/build/.ccache; export CCACHE_DIR
+unset CCACHE_HARDLINK
+CCACHE_COMPRESS=t; export CCACHE_COMPRESS
+CCACHE_UMASK=002; export CCACHE_UMASK
+
+## Hack the build options.  `sbuild' tries to turn off testing for
+## cross-builds, which is exactly wrong.  Turn them back on unless I really
+## want them off.
+old=$DEB_BUILD_OPTIONS new= force_nocheck=nil
+for o in $old; do
+  case $o in x-mdw-nocheck) force_nocheck=t ;; esac
+done
+for o in $old; do
+  include=t
+  case $o in
+    x-mdw-nocheck) include=nil ;;
+    nocheck) include=$force_nocheck ;;
+  esac
+  case $include in
+    t) new=${new:+$new }$o ;;
+  esac
+done
+DEB_BUILD_OPTIONS=$new; export DEB_BUILD_OPTIONS
+
+## Preset the library search path to find the tools version of `fakeroot'.
+for i in /usr/lib/*/libfakeroot; do
+  LD_LIBRARY_PATH=${LD_LIBRARY_PATH+$LD_LIBRARY_PATH:}$i
+done
+export LD_LIBRARY_PATH
+
+## We're ready to go.
+exec "$@"
diff --git a/bin/install-cross-tools b/bin/install-cross-tools
new file mode 100755 (executable)
index 0000000..7673229
--- /dev/null
@@ -0,0 +1,202 @@
+#! /bin/sh -e
+###
+### Replace common tools in foreign chroots with native versions
+###
+### (c) 2018 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk chroot maintenance tools.
+###
+### distorted-chroot is free software: you can redistribute it and/or
+### modify it under the terms of the GNU General Public License as
+### published by the Free Software Foundation; either version 2 of the
+### License, or (at your option) any later version.
+###
+### distorted-chroot is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+### General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with distorted-chroot.  If not, write to the Free Software
+### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+### USA.
+
+. state/config.sh # @@@config@@@
+
+## Parse the command-line.
+badp=nil
+case $# in 2) ;; *) badp=t ;; esac
+case $badp in t) echo >&2 "usage: $0 DIST ARCH"; exit 2 ;; esac
+d=$1 a=$2
+
+## Figure out of all the architecture names.
+mymulti=$(dpkg-architecture -a$TOOLSARCH -qDEB_HOST_MULTIARCH)
+gnuarch=$(dpkg-architecture -A$a -qDEB_TARGET_GNU_TYPE)
+qarch=nil qhost=nil
+for fa in $FOREIGN_ARCHS; do
+  case $fa in
+    "$a")
+      eval qarch=\$${a}_QEMUARCH qhost=\$${a}_QEMUHOST
+      break ;;
+  esac
+done
+case $qarch,$qhost in
+  nil,* | *,nil) echo >&2 "$0: not a foreign architecture"; exit 2 ;;
+esac
+
+## Make sure we have the tools we need.
+crossdir=/usr/local.schroot/cross/$d-$TOOLSARCH
+my_crossdir=$LOCAL/${crossdir#/usr/local.schroot/}
+qemudir=/usr/local.schroot/cross/$d-$qhost/QEMU
+my_qemudir=$LOCAL/${qemudir#/usr/local.schroot/}
+if ! [ -d $my_crossdir ]; then
+  echo 2>&1 "$0: no tree for \`$d-$TOOLSARCH'"; exit 2
+fi
+if ! [ -d $my_qemudir ]; then
+  echo 2>&1 "$0: no tree for \`$d-$qhost'"; exit 2
+fi
+
+## Open a session to the target chroot.
+sess=$(schroot -bcsource:$LVPREFIX$d-$a)
+root=/schroot/$sess/fs
+
+## Abuse `/mnt/' as a temporary staging area.  This saves us from clobbering
+## the chroot with weird directories.
+schroot -uroot -rc$sess -- sh -ec '
+       if ! mountpoint -q /mnt; then
+         mount -ttmpfs -omode=700,uid=0,gid=0 private /mnt
+       fi'
+
+## Work through all of the tools we're interested in, to decide what we need
+## to do with it.  Make lists:
+##
+##   * `DIVERT.want' lists all of the filenames which need dpkg diversions to
+##     prevent foreign versions of the tools from clobbering our native
+##     versions.
+##
+##   * `LINK'.want' lists all of the paths which need symbolic links into the
+##     cross-tools trees, together with the link destinations.
+{ echo $qemudir/qemu-$qarch-static
+  echo $crossdir/lib/$mymulti
+  echo $crossdir/usr/lib/$mymulti
+  echo $crossdir/usr/lib/gcc-cross
+  find $my_crossdir $my_crossdir/TOOLCHAIN/$gnuarch \
+       \( \(  -path "*/QEMU" -o -path "*/TOOLCHAIN" -o \
+       -path "*/lib/$mymulti" -o \
+       -path "*/lib/gcc-cross" \) -prune \) -o \
+       \( ! -type d -print \)
+} | sed "s\a^$LOCAL/\a/usr/local.schroot/\a" | while read t; do
+  case $t in
+    $qemudir/*)
+      s=/usr/bin/${t#$qemudir/} ;;
+    $crossdir/TOOLCHAIN/$gnuarch/*)
+      s=/usr/bin/${t#$crossdir/TOOLCHAIN/$gnuarch/} ;;
+    *)
+      s=${t#$crossdir} ;;
+  esac
+  if [ -L $t ]; then t=$(readlink $t); fi
+  if [ -d $t ]; then act=LINK; else act=DIVERT; fi
+  echo $act $s $t
+done >$root/mnt/ALL.want
+sed -n '/^DIVERT \(.*\) .*$/s//\1/p' $root/mnt/ALL.want | \
+  sort >$root/mnt/DIVERT.want
+sed -n '/^\(DIVERT\|LINK\) /s///p' $root/mnt/ALL.want | \
+  sort >$root/mnt/LINK.want
+
+## Make a list, `DIVERT.have', of paths which already have diversions, and a
+## list `LINK.have' of symbolic links into the cross-tools trees.  The
+## layouts of these files match the `.want' files we just made.
+schroot -uroot -rc$sess -- sh -ec '
+       dpkg-divert --list |
+         sed -n "/^diversion of \(.*\) to .* by install-cross-tools\$/s//\1/p" | \
+         sort >/mnt/DIVERT.have
+       { find / -xdev -lname "/usr/local.schroot/cross/*" -printf "%p %l\n"
+         while read s _; do
+           if ! [ -L "$s" ]; then continue; fi
+           t=$(readlink $s)
+           case $t in /usr/local.schroot/cross/*) continue ;; esac
+           echo "$s $t"
+         done </mnt/LINK.want
+       } | sort >/mnt/LINK.have'
+
+## Add diversions for the paths which need one, but don't have one.  There's
+## a hack here because the `--no-rename' option was required in the same
+## version in which is was introduced, so there's no single incantation that
+## will work across the boundary.
+schroot -uroot -rc$sess -- sh -ec '
+       a=$1
+
+       if dpkg-divert >/dev/null 2>&1 --no-rename --help
+       then no_rename=--no-rename
+       else no_rename=
+       fi
+
+       comm -13 /mnt/DIVERT.have /mnt/DIVERT.want | while read i; do
+         dpkg-divert --package "install-cross-tools" $no_rename \
+           --divert "$i.$a" --add "$i"
+       done' - $a
+
+## Go through each diverted tool, and, if it hasn't been moved aside, then
+## /link/ it across now.  If we rename it, then the chroot will stop working
+## -- which is why we didn't allow `dpkg-divert' to do the rename.  We can
+## tell a tool that hasn't been moved, because it's a symlink into one of the
+## cross trees.
+while read i; do
+  if [ -e $root$i ] && ! [ -e $root$i.$a ]; then
+    if [ -L $root$i ]; then
+      t=$(readlink $root$i)
+      case $t in
+       $crossdir/* | $qemudir/* | /usr/local.schroot/qemu/*) continue ;;
+      esac
+      if [ -L $crossdir$i ]; then
+       u=$(readlink $crossdir$i)
+       case $t in "$u") continue ;; esac
+      fi
+    fi
+    echo >&2 "$0: preserve old $i"
+    ln $root$i $root$i.$a
+  fi
+done <$root/mnt/DIVERT.want
+
+## Update all of the symbolic links which are currently wrong: add links
+## which are missing, delete ones which are obsolete, and update ones which
+## have the wrong target.
+join -j1 -a1 -a2 -e- -o"0 1.2 2.2" \
+     $root/mnt/LINK.have $root/mnt/LINK.want |
+  while read s t0 t1; do
+    case $t1 in
+      "$t0")
+       continue
+       ;;
+      -)
+       echo >&2 "$0: remove obsolete link $s -> $t0"
+       rm -f $root$s
+       ;;
+      *)
+       case $s in */*) mkdir -p $root${s%/*} ;; esac
+       rm -f $root$s.new
+       ln -s $t1 $root$s.new
+       echo >&2 "$0: link $s -> $t1"
+       mv -T $root$s.new $root$s
+       ;;
+    esac
+  done
+
+## Remove diversions from paths which don't need them any more.  Here it's
+## safe to rename, because either the tool isn't there, in which case it
+## obviously wasn't important, or it is, and `dpkg-divert' will atomically
+## replace our link with the foreign version.
+schroot -uroot -rc$sess -- sh -ec '
+       a=$1
+       comm -23 /mnt/DIVERT.have /mnt/DIVERT.want | while read i; do
+         dpkg-divert --package "install-cross-tools" --rename \
+           --divert "$i.$a" --remove "$i"
+       done' - $a
+
+## Close the session.
+schroot -ec$sess
+
+###----- That's all, folks --------------------------------------------------
index 40f925b..58dc7c6 100755 (executable)
 
 . state/config.sh # @@@config@@@
 
+## Convert the PROXY configuration setting into something that will affect
+## `debootstrap'.
 case $PROXY in nil) ;; *) http_proxy=$PROXY; export PROXY ;; esac
 
+## Parse the command-line.
 badp=nil forcep=nil
 while getopts "f" opt; do
   case $opt in
@@ -43,19 +46,22 @@ d=$1 a=$2
 case $d-$a in *-*-*) echo >&2 "$0: bad chroot name \`$arg'"; exit 2 ;; esac
 if [ ! -d /dev/$VG/ ]; then echo >&2 "$0: no volume group \`$VG'"; exit 2; fi
 
+## Decide whether we need to do special things for installing a foreign
+## architecture.
 qemup=nil dbsopts=
 for fa in $FOREIGN_ARCHS; do
   case $fa in
     "$a")
-      qemup=t qbsopts=--foreign
-      eval qhost=\$${a}_QEMUARCH qhost=\$${a}_QEMUHOST
+      qemup=t dbsopts=--foreign
+      eval qarch=\$${a}_QEMUARCH qhost=\$${a}_QEMUHOST
       break
       ;;
   esac
 done
 
+## Construct the logical volume and lay a filesystem onto it.
 lv=$LVPREFIX$d-$a
-mnt=mnt/$lv
+mnt=$HERE/mnt/$lv
 mkdir -p $mnt
 if mountpoint -q $mnt; then umount $mnt; fi
 if [ -b /dev/$VG/$lv ]; then
@@ -69,10 +75,19 @@ mkfs -j -L$d-$a /dev/$VG/$lv
 mount -orelatime,data=writeback,commit=3600,barrier=0 /dev/$VG/$lv $mnt/
 mkdir -m755 $mnt/fs/
 chmod 750 $mnt/
-pkgs=ccache,eatmydata,fakeroot,libfile-fcntllock-perl,locales,tzdata
+
+## Install the base system.
+want=$BASE_PACKAGES
+case $qemup in
+  t) want="$want $FOREIGN_BASE_PACKAGES" ;;
+  nil) want="$want $NATIVE_BASE_PACKAGES" ;;
+esac
+pkgs=; for p in $want; do pkgs=${pkgs:+$pkgs,}$p; done
 eatmydata debootstrap $dbsopts --arch=$a --variant=minbase \
          --include=$pkgs $d $mnt/fs/ $DEBMIRROR
 
+## If this is a cross-installation, then install the necessary `qemu' and
+## complete the installation.
 case $qemup in
   t)
     install $LOCAL/cross/$d-$qhost/QEMU/qemu-$qarch-static \
@@ -83,16 +98,17 @@ case $qemup in
     ;;
 esac
 
-cd $mnt/fs/usr/
-rm -rf local/; ln -s local.schroot/$a local
+## Set up `/usr/local'.
+rm -rf $mnt/fs/usr/local/; ln -s local.schroot/$a $mnt/fs/usr/local
 
-cd $mnt/fs/etc/apt/
-rm -rf apt.conf sources.list
-ln -s /usr/local.schroot/config/apt/conf.d/10sbuild apt.conf.d/
-ln -s /usr/local.schroot/config/apt/conf.d/90local apt.conf.d/
-ln -s /usr/local.schroot/config/apt/sources.$d sources.list
+## Install the `apt' configuration.
+rm -rf $mnt/fs/etc/apt/apt.conf $mnt/fs/etc/apt/sources.list
+for c in $LOCAL/etc/apt/apt.conf.d/*; do
+  ln -s /usr/local.schroot/${c#$LOCAL/} $mnt/fs/etc/apt/apt.conf.d/
+done
+ln -s /usr/local.schroot/etc/apt/sources.$d $mnt/fs/etc/apt/sources.list
 
-cat >apt.conf.d/20arch <<EOF
+cat >$mnt/fs/etc/apt/apt.conf.d/20arch <<EOF
 ### -*-conf-*-
 
 APT {
@@ -100,37 +116,42 @@ APT {
 };
 EOF
 
-cd $mnt/fs/etc/
-cp /etc/locale.gen /etc/timezone ./
-tz=$(cat timezone); ln -sf /usr/share/zoneinfo/$tz localtime
-ln -sf /proc/mounts mtab
+## Set up the locale and time zone from the host system.
+cp /etc/locale.gen /etc/timezone $mnt/fs/etc/
+tz=$(cat /etc/timezone); ln -sf /usr/share/zoneinfo/$tz $mnt/fs/etc/localtime
+ln -sf /proc/mounts $mnt/fs/etc/mtab
 
-cd $mnt/fs/etc/default/
-cp /etc/default/locale .
+cp /etc/default/locale $mnt/fs/etc/default/
 
-cd $mnt/fs/usr/sbin/
-cat >policy-rc.d <<EOF
+## Prevent daemons from starting within the chroot.
+cat >$mnt/fs/usr/sbin/policy-rc.d <<EOF
 #! /bin/sh
 echo >&2 "policy-rc.d: Services disabled by policy."
 exit 101
 EOF
-chmod +x policy-rc.d
+chmod +x $mnt/fs/usr/sbin/policy-rc.d
 
-cd $mnt/fs/etc/ld.so.conf.d/
-cat >libc.conf <<EOF
+## Hack the dynamic linker to prefer libraries in `/usr' over `/usr/local'.
+cat >$mnt/fs/etc/ld.so.conf.d/libc.conf <<EOF
 # libc default configuration
 EOF
-cat >zzz-local.conf <<EOF
+cat >$mnt/fs/etc/ld.so.conf.d/zzz-local.conf <<EOF
 ### -*-conf-*-
 ### Local hack to make /usr/local/ late.
 /usr/local/lib
 EOF
 
-cd /
+## We're done setting the chroot environment up.
 umount $mnt/
 
+## If this is a foreign architecture then we need to set it up.
 case $qemup in
   t)
+    ## Keep the chroot's native Qemu out of our way: otherwise we'll stop
+    ## being able to run programs in the chroot.  There's a hack here because
+    ## the `--no-rename' option was required in the same version in which is
+    ## was introduced, so there's no single incantation that will work across
+    ## the boundary.
     schroot -uroot -csource:$LVPREFIX$d-$a -- eatmydata sh -e -c "
        if dpkg-divert >/dev/null 2>&1 --no-rename --help
        then no_rename=--no-rename
@@ -138,11 +159,18 @@ case $qemup in
        fi
 
        dpkg-divert --package install-cross-tools \$no_rename \
-         --divert /usr/bin/$qemu.$a --add /usr/bin/$qemu"
+         --divert /usr/bin/qemu-$qarch-static.$a --add /usr/bin/qemu-$qarch-static"
+
+    ## Install faster native tools.
     $STATE/bin/install-cross-tools $d $a
+
+    ## Install `build-essential', which had been delayed from earlier.
+    schroot -uroot -csource:$LVPREFIX$d-$a -- \
+           eatmydata apt-get -y install build-essential
     ;;
 esac
 
+## Set the chroot's package state up properly.
 schroot -uroot -csource:$LVPREFIX$d-$a -- eatmydata sh -e -c '
        apt-get update
        apt-get -y upgrade
index 9aff2a6..642fb37 100755 (executable)
@@ -96,7 +96,7 @@ EOF
 type=lvm-snapshot
 description=Debian $dist/$arch autobuilder
 device=/dev/$VG/$LVPREFIX$dist-$arch
-lvm-snapshot-options=$snapopt
+lvm-snapshot-options=$SNAPOPT
 mount-options=-onosuid,data=writeback,barrier=0,commit=3600,noatime
 location=/fs
 groups=root,sbuild
diff --git a/bin/update-cross-tools b/bin/update-cross-tools
new file mode 100755 (executable)
index 0000000..a738af0
--- /dev/null
@@ -0,0 +1,277 @@
+#! /bin/sh -e
+###
+### Fetch native versions of tools for insertion into foreign chroots
+###
+### (c) 2018 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk chroot maintenance tools.
+###
+### distorted-chroot is free software: you can redistribute it and/or
+### modify it under the terms of the GNU General Public License as
+### published by the Free Software Foundation; either version 2 of the
+### License, or (at your option) any later version.
+###
+### distorted-chroot is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+### General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with distorted-chroot.  If not, write to the Free Software
+### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+### USA.
+
+. state/config.sh # @@@config@@@
+
+###--------------------------------------------------------------------------
+### Utilities.
+
+chase_link () {
+  p=$1 d=
+  ## Copy an absolute path P from the donor tree `$root/' into the
+  ## cross-tools tree `$crossnew/'.  If resolving P involves traversing a
+  ## symbolic link, then ensure that the pieces of filesystem it directs us
+  ## to are also copied.
+
+  ## Work through the remaining components of the path.
+  while :; do
+
+    ## Analyse the first remaining component of the path P.
+    case $p in
+
+      ## It's empty.  We're done.
+      "") break ;;
+
+      ## A redundant `/' or `./'.  Skip it.
+      "/"*) p=${p#/} ;;
+      "./"*) p=${p#./} ;;
+
+      ## A `../'.  Strip off the trailing component of D.
+      "../"*)
+       p=${p#../}
+       case $d in */*) d=${d%/*} ;; *) d= ;; esac
+       ;;
+
+      ## Something else.  Transfer the component name to D.
+      *)
+       case $p in */*) f=${p%%/*} p=${p#*/} ;; *) f=$p p="" ;; esac
+       d=${d:+$d/}$f
+       ;;
+    esac
+
+    ## If D doesn't refer to a file in the cross-tools tree, then maybe it
+    ## refers to something in the donor tree.  Find out what, and copy it
+    ## into the cross-tools tree.
+    if ! [ -e "$crossnew$d" ] && ! [ -L "$crossnew$d" ]; then
+      if [ -d "$root/$d" ] && ! [ -L "$root/$d" ]; then
+       mkdir "$crossnew$d"
+      else
+       echo >&2 "$0: copy /$d to satisfy symlinks"
+       rsync -aHR $root/./$d $crossnew
+      fi
+    fi
+
+    ## If D refers to a symbolic link, then append the link target to P, so
+    ## that we can make sure we copy the target.
+    if [ -L "$crossnew$d" ]; then
+      t=$(readlink "$crossnew$d")
+      case $t in /*) t=${t#/} d= ;; esac
+      case $d in */*) d=${d%/*} ;; *) d= ;; esac
+      p=$t${p:+/$p}
+    fi
+  done
+}
+
+###--------------------------------------------------------------------------
+### Main program.
+
+## Parse the command line.
+badp=nil
+case $# in 2) ;; *) badp=t ;; esac
+case $badp in t) echo >&2 "usage: $0 DIST MYARCH"; exit 2 ;; esac
+d=$1 myarch=$2
+
+## Keep track of our original stdout.
+exec 3>&1
+
+## Figure out derived architecture names.
+mymulti=$(dpkg-architecture -a$myarch -qDEB_HOST_MULTIARCH)
+
+## First, set `cross_archs' as a list of GNUish names for our supported
+## foreign architectures.
+cross_archs="arm-linux-gnueabi arm-linux-gnueabihf aarch64-linux-gnu"
+
+## Make a list of extra packages we'll need to install to obtain our tools.
+cross_pkgs="
+       apt bash ccache coreutils dash eatmydata fakeroot findutils
+       gnupg gpgv gzip m4 make mawk qemu-user-static sed tar xz-utils"
+for a in $cross_archs; do
+  for i in gcc g++ binutils; do
+    cross_pkgs="$cross_pkgs $i-$a"
+  done
+done
+cross_pkgs=$(echo $cross_pkgs)
+
+## Make an enormous shopping list of paths.
+##
+## The `wanted' list consists of two kinds of items: an absolute path names a
+## prefix (not necessarily a directory name) to be attached to the following
+## relative names, up to the end of the list or the next absolute path.
+wanted="
+       /usr/bin/ apt apt-cache apt-config apt-get apt-key apt-mark
+       /usr/lib/apt/ methods/ solvers/
+
+       /bin/ cat chgrp chown cp date dd df dir echo false ln ls mkdir
+               mknod mktemp mv pwd readlink rm rmdir sleep stty sync touch
+               true uname vdir
+       /usr/bin/ [ arch b2sum base32 base64 basename chcon cksum comm
+               csplit cut dircolors dirname du env expand expr factor fmt
+               fold groups head hostid id install join link logname md5sum
+               mkfifo nice nl nohup nproc numfmt od paste pathchk pinky pr
+               printenv printf ptx realpath runcon seq sha1sum sha224sum
+               sha256sum sha384sum sha512sum shred shuf sort split stat
+               stdbuf sum tac tail tee test timeout tr truncate tsort tty
+               unexpand uniq unlink users wc who whoami yes
+       /usr/lib/$mymulti/ coreutils/
+
+       /lib/$mymulti/ libnss_*.so.*
+
+       /usr/bin/ gpg gpgv gpgconf kbxutil watchgnupg
+
+       /usr/bin/ qemu-*-static
+
+       /bin/ bash dash gzip sed tar
+       /usr/bin/ ccache find m4 make mawk xargs xz
+       /usr/lib/$mymulti/ libeatmydata.so* libfakeroot/
+
+       /etc/ld.so.conf.d/ $mymulti.conf fakeroot*.conf"
+
+for a in $cross_archs; do
+  wanted="$wanted
+
+       /usr/bin/$a- addr2line ar as c++filt dwp elfedit gprof ld ld.*
+               nm objcopy objdump ranlib readelf size strings strip
+
+       /usr/bin/$a- cpp gcc g++ gcov gcov-dump gcov-tool gprof
+               gcc-ar gcc-nm gcc-ranlib
+       /usr/lib/gcc-cross/$a/ ..."
+done
+wanted=$(echo $wanted)
+
+## Figure out how to recognize dynamic executables.
+case $myarch in
+  i386) elfsig=7f454c46010101??0000000000000000????0300 ;;
+  amd64) elfsig=7f454c46020101??0000000000000000????3e00 ;;
+  *) echo >&2 "$0: unsupported local arch \`$myarch'"; exit 2 ;;
+esac
+
+## Open a session to the donor chroot.
+echo >&2 "$0: create $d snapshot"
+sess=$(schroot -bc$LVPREFIX$d-$myarch 3>&-)
+
+## Make sure the donor tree is up-to-date, and install the extra packages we
+## need.
+schroot -uroot -rc$sess -- eatmydata sh -ec "
+       apt-get update
+       apt-get -y upgrade
+       apt-get -y install $cross_pkgs"
+
+## Establish some pathnames.  Prepare a place for our cross-tools tree.
+crossdir=$LOCAL/cross/$d-$myarch/
+crossold=${crossdir%/}.old/ crossnew=${crossdir%/}.new/
+root=/schroot/$sess/fs
+rm -rf $crossnew; mkdir -p $crossnew
+
+## Work through the shopping list, copying the things it names into the
+## cross-tools tree.
+dir=/
+for i in $wanted; do
+  case $i in
+    /*)
+      dir=$i
+      ;;
+    *)
+      case $i in ...) f=$dir ;; *) f=$dir$i ;; esac
+      echo >&2 "$0: copy $f"
+      rsync -aHR $root/.$f $crossnew
+      ;;
+  esac
+done
+
+## Chase links in the new tree, copying extra stuff that we'll need.
+find $crossnew -xtype l -print | while read i; do
+  chase_link ${i#$crossnew}
+done
+
+## Search the new tree for ELF binaries, and build a list of them in
+## `QUEUE.in'.
+find $crossnew -type f -print | while read i; do
+  sig=$(head -c20 "$i" | bincode -e -m0 -flowerc hex)
+  case $sig in $elfsig) echo "$i" ;; esac
+done >$root/private/QUEUE.in
+
+while [ -s $root/private/QUEUE.in ]; do
+  ## Work through the ELF binaries in `QUEUE.in', determining which shared
+  ## libraries they'll need.  Write the list of dependencies to `QUEUE.out'
+  schroot -uroot -rc$sess -- eatmydata sh -ec '
+       prog=$1
+       while read i; do
+       echo >&2 "$prog: scanning binary $i"
+         ldd "$i" | while read a b c d; do
+           case $a:$b:$c:$d in
+             not:a:dynamic:executable) ;;
+             statically:linked::) ;;
+             /*) echo "$a" ;;
+             *:=\>:/*) echo "$c" ;;
+             linux-*) ;;
+             *) echo >&2 "$i: unable to find $a"; exit 2 ;;
+           esac
+         done
+       done </private/QUEUE.in >/private/QUEUE.out' - "$0"
+
+  ## Work through the shared libraries in `QUEUE.out', copying them to the
+  ## cross-tools tree if they're not there already.  Add the new ones to a
+  ## new `QUEUE.in' file to scan them in turn.
+  while read i; do
+    if [ -e "$crossnew$i" ] || [ -L "$crossnew$i" ]
+    then continue; fi
+    if [ -d "$root$i" ]; then continue; fi
+    echo >&2 "$0: copy $i"
+    rsync -aHR $root/.$i $crossnew >&3
+    chase_link $i >&3
+    sig=$(head -c20 $crossnew$i | bincode -e -m0 -flowerc hex)
+    case $sig in $elfsig) echo "$i" ;; esac
+  done <$root/private/QUEUE.out >$root/private/QUEUE.in
+done
+
+## Set up the cross-compiler.  This is rather hairy.
+echo >&2 "$0: establish TOOLCHAIN"
+for a in $cross_archs; do
+  tooldir=$crossnew/TOOLCHAIN/$a
+  mkdir -p $tooldir
+  for i in $crossnew/usr/bin/$a-*; do
+    t=${i#$crossnew/usr/bin/}
+    mv $i $tooldir/$t
+    ln -s $t $tooldir/${t#$a-}
+  done
+done
+mkdir $crossnew/TOOLCHAIN/lib
+ln -s ../../usr/lib/gcc-cross $crossnew/TOOLCHAIN/lib/
+
+## Set up the emulator.
+echo >&2 "$0: establish QEMU"
+mkdir $crossnew/QEMU
+mv $crossnew/usr/bin/qemu-*-static $crossnew/QEMU/
+
+## We're done.  Remove the snapshot, and replace the old cross-tools tree
+## with our new one.
+echo >&2 "$0: remove snapshot"
+schroot -ec$sess 3>&-
+if [ -d $crossdir ]; then mv $crossdir $crossold; fi
+mv $crossnew $crossdir; rm -rf $crossold
+echo >&2 "$0: committed $crossdir"
+
+###----- That's all, folkd --------------------------------------------------
diff --git a/etc/schroot-scripts/11private b/etc/schroot-scripts/11private
new file mode 100755 (executable)
index 0000000..c477ddf
--- /dev/null
@@ -0,0 +1,53 @@
+#! /bin/sh -e
+###
+### Make build trees private to the invoking group
+###
+### (c) 2018 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk chroot maintenance tools.
+###
+### distorted-chroot is free software: you can redistribute it and/or
+### modify it under the terms of the GNU General Public License as
+### published by the Free Software Foundation; either version 2 of the
+### License, or (at your option) any later version.
+###
+### distorted-chroot is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+### General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with distorted-chroot.  If not, write to the Free Software
+### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+### USA.
+
+### Make a build tree private to the invoking user.  Also, make a `/private'
+### directory in the chroot which is exclusive to the creating user.
+
+## Make sure everything is good.
+case $1 in setup-start) ;; *) exit 0 ;; esac
+case $CHROOT_SESSION_PURGE in true) ;; *) exit 0 ;; esac
+case $CHROOT_PROFILE in sbuild | scratchbox) ;; *) exit 0 ;; esac
+case $CHROOT_TYPE in *-snapshot) ;; *) exit 0 ;; esac
+case $CHROOT_MOUNT_LOCATION in
+  "" | /) echo >&2 "$0: not clobbering root dir"; exit 127 ;;
+esac
+
+## Make the directory private to the invoking user's group.  This is a
+## somewhat troublesome compromise between keeping the chroot tree private
+## from other system users on the one hand, and maintaining system security
+## on the other.
+##
+## This assumes that the device root directory's permissions are already
+## restricted to privileged users only.
+cd $CHROOT_MOUNT_LOCATION
+chown root:$AUTH_RGROUP .
+chmod 750 .
+
+## Make an actually-private place for temporary things to be stored.
+mkdir -p $CHROOT_PATH/private
+mount -ttmpfs -omode=700,uid=$AUTH_RUID,gid=$AUTH_RGID \
+  private $CHROOT_PATH/private
diff --git a/etc/schroot-scripts/15binfmt b/etc/schroot-scripts/15binfmt
new file mode 100755 (executable)
index 0000000..892f046
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+## We've already handled this.
+:
diff --git a/etc/schroot-scripts/51chrootenv b/etc/schroot-scripts/51chrootenv
new file mode 100755 (executable)
index 0000000..797b5d3
--- /dev/null
@@ -0,0 +1,33 @@
+#! /bin/sh -e
+###
+### Write the chroot environment variables to a file.
+###
+### (c) 2018 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk chroot maintenance tools.
+###
+### distorted-chroot is free software: you can redistribute it and/or
+### modify it under the terms of the GNU General Public License as
+### published by the Free Software Foundation; either version 2 of the
+### License, or (at your option) any later version.
+###
+### distorted-chroot is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+### General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with distorted-chroot.  If not, write to the Free Software
+### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+### USA.
+
+### Otherwise this useful information gets lost.
+
+case "$1" in
+  setup-start)
+    env | sed -n '/^CHROOT_/s///p' | sort >"$CHROOT_PATH/etc/schroot.info"
+    ;;
+esac
diff --git a/pkg/checkpath-1.2.4.1.tar.gz b/pkg/checkpath-1.2.4.1.tar.gz
new file mode 100644 (file)
index 0000000..9619830
Binary files /dev/null and b/pkg/checkpath-1.2.4.1.tar.gz differ
diff --git a/pkg/mLib-2.3.3.1.tar.gz b/pkg/mLib-2.3.3.1.tar.gz
new file mode 100644 (file)
index 0000000..63ba286
Binary files /dev/null and b/pkg/mLib-2.3.3.1.tar.gz differ