diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 40c0be1c2cfb..0b70acdb72d0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -197,7 +197,9 @@ jobs: # Generate / upload artifact. {{{ - name: Generate artifact - run: make update --assume-old=base + # Note: ensure the build skeleton is created, so there are no dangling + # symlinks (e.g. `data/dict`) to trip 7zip during artifact generation… + run: make skeleton update --assume-old=base - name: Upload artifact uses: actions/upload-artifact@v4 diff --git a/Makefile b/Makefile index fad49a6170a8..54e609f21cd0 100644 --- a/Makefile +++ b/Makefile @@ -14,10 +14,6 @@ ifneq (,$(findstring -,$(VERSION))) VERSION := $(VERSION)_$(RELEASE_DATE) endif -# releases do not contain tests and misc data -IS_RELEASE := $(if $(or $(EMULATE_READER),$(WIN32)),,1) -IS_RELEASE := $(if $(or $(IS_RELEASE),$(APPIMAGE),$(LINUX),$(MACOS)),1,) - LINUX_ARCH?=native ifeq ($(LINUX_ARCH), native) LINUX_ARCH_NAME:=$(shell uname -m) @@ -50,11 +46,14 @@ WIN32_DIR=$(PLATFORM_DIR)/win32 define CR3GUI_DATADIR_EXCLUDES %/KoboUSBMS.tar.gz +%/NOTES.txt %/cr3.ini %/cr3skin-format.txt %/desktop %/devices +%/dict %/manual +%/tessdata endef CR3GUI_DATADIR_FILES = $(filter-out $(CR3GUI_DATADIR_EXCLUDES),$(wildcard $(CR3GUI_DATADIR)/*)) @@ -71,18 +70,78 @@ INSTALL_FILES=reader.lua setupkoenv.lua frontend resources defaults.lua datastor OUTPUT_DIR_ARTIFACTS = $(abspath $(OUTPUT_DIR))/!(cache|cmake|data|history|staging|thirdparty) OUTPUT_DIR_DATAFILES = $(OUTPUT_DIR)/data/* +# Release excludes. {{{ + +define UPDATE_PATH_EXCLUDES +cache +clipboard +data/dict +data/tessdata +ev_replay.py +help +history +l10n/templates +ota +resources/fonts* +resources/icons/src* +screenshots +spec +endef + +# Files created after execution. +define UPDATE_PATH_EXCLUDES += +data/cr3.ini +defaults.*.lua +history.lua +scripts +settings +settings.reader.lua* +styletweaks +version.log +endef + +# Testsuite leftovers. +define UPDATE_PATH_EXCLUDES += +dummy-test-file* +file.sdr* +readerbookmark.* +readerhighlight.* +testdata +this-is-not-a-valid-file* +endef + +# Globally excluded. +define UPDATE_GLOBAL_EXCLUDES +*.orig +*.swo +*.swp +*.un~ +.* +endef + +release_excludes = $(strip $(UPDATE_PATH_EXCLUDES:%='-x!$1%') $(UPDATE_GLOBAL_EXCLUDES:%='-xr!%')) + +# }}} + +define mkupdate +cd $(INSTALL_DIR) && +'$(abspath tools/mkrelease.sh)' +$(if $(PARALLEL_JOBS),--jobs $(PARALLEL_JOBS)) +--manifest=$(or $2,koreader)/ota/package.index +$(foreach a,$1,'$(if $(filter --%,$a),$a,$(abspath $a))') $(or $2,koreader) +$(call release_excludes,$(or $2,koreader)/) +endef + all: base install -d $(INSTALL_DIR)/koreader rm -f $(INSTALL_DIR)/koreader/git-rev; echo "$(VERSION)" > $(INSTALL_DIR)/koreader/git-rev ifdef ANDROID rm -f android-fdroid-version; echo -e "$(ANDROID_NAME)\n$(ANDROID_VERSION)" > koreader-android-fdroid-latest endif -ifeq (,$(IS_RELEASE)) $(SYMLINK) $(KOR_BASE)/ev_replay.py $(INSTALL_DIR)/koreader/ -endif bash -O extglob -c '$(SYMLINK) $(OUTPUT_DIR_ARTIFACTS) $(INSTALL_DIR)/koreader/' ifneq (,$(EMULATE_READER)) - @echo "[*] install front spec only for the emulator" + @echo "[*] Install front spec only for the emulator" $(SYMLINK) spec $(INSTALL_DIR)/koreader/spec/front $(SYMLINK) test $(INSTALL_DIR)/koreader/spec/front/unit/data endif @@ -112,10 +171,6 @@ endif ! test -L $(INSTALL_DIR)/koreader/data || rm $(INSTALL_DIR)/koreader/data install -d $(INSTALL_DIR)/koreader/data $(SYMLINK) $(strip $(DATADIR_FILES)) $(INSTALL_DIR)/koreader/data/ -ifneq (,$(IS_RELEASE)) - @echo "[*] Clean up, remove unused files for releases" - rm -rf $(INSTALL_DIR)/koreader/data/{cr3.ini,desktop,devices,dict,manual,tessdata} -endif base: base-all @@ -198,3 +253,5 @@ doc: .PHONY: $(PHONY) include $(KOR_BASE)/Makefile + +# vim: foldmethod=marker foldlevel=0 diff --git a/make/android.mk b/make/android.mk index 40b87e3f395c..9b37262e4202 100644 --- a/make/android.mk +++ b/make/android.mk @@ -42,6 +42,12 @@ ANDROID_DIR = $(PLATFORM_DIR)/android ANDROID_LAUNCHER_DIR = $(ANDROID_DIR)/luajit-launcher ANDROID_LAUNCHER_BUILD = $(INSTALL_DIR)/luajit-launcher ANDROID_ASSETS = $(ANDROID_LAUNCHER_BUILD)/assets +# Assets compression method: +# - LZMA/LZMA2: `-m0=lzma2 -mx=9` +# - LZMA/LZMA2 (7z >= 17.02, fast version): `-m0=flzma2 -mx=9` +# - ZSTD (7z >= 17.02, LZMA still used for archive headers): `-m0=zstd -mx=16` +# - ZSTD (7z >= 17.02): `-m0=zstd -mhc=off -mx=16` +ANDROID_ASSETS_COMPRESSION ?= -m0=lzma2 -mx=9 ANDROID_LIBS = $(ANDROID_LAUNCHER_BUILD)/libs/$(ANDROID_ABI) ANDROID_FLAVOR ?= Rocks @@ -61,6 +67,22 @@ else ANDROID_ABI ?= armeabi-v7a endif +define UPDATE_PATH_EXCLUDES += +plugins/SSH.koplugin +plugins/autofrontlight.koplugin +plugins/hello.koplugin +plugins/timesync.koplugin +tools +endef + +define UPDATE_GLOBAL_EXCLUDES += +README.md +COPYING +LICENSE* +*license.txt +NOTICE +endef + androiddev: update $(MAKE) -C $(ANDROID_LAUNCHER_DIR) dev @@ -83,35 +105,14 @@ update: all # binaries are stored as shared libraries to prevent W^X exception on Android 10+ # https://developer.android.com/about/versions/10/behavior-changes-10#execute-permission llvm-strip --strip-unneeded $(INSTALL_DIR)/koreader/sdcv -o $(ANDROID_LIBS)/libsdcv.so - # assets are compressed manually and stored inside the APK. + # Assets are compressed manually and stored inside the APK. cd $(INSTALL_DIR)/koreader && \ - ./tools/mk7z.sh \ - $(abspath $(ANDROID_ASSETS)/module/koreader.7z) \ - "$$(git show -s --format='%ci')" \ - -m0=lzma2 -mx=9 \ - -- . \ - '-x!cache' \ - '-x!clipboard' \ - '-x!data/dict' \ - '-x!data/tessdata' \ - '-x!history' \ - '-x!l10n/templates' \ - '-x!libs' \ - '-x!ota' \ - '-x!resources/fonts*' \ - '-x!resources/icons/src*' \ - '-x!rocks/bin' \ - '-x!rocks/lib/luarocks' \ - '-x!screenshots' \ - '-x!sdcv' \ - '-x!spec' \ - '-x!tools' \ - '-xr!.*' \ - '-xr!COPYING' \ - '-xr!NOTES.txt' \ - '-xr!NOTICE' \ - '-xr!README.md' \ - ; + ./tools/mkrelease.sh \ + $(if $(PARALLEL_JOBS),--jobs $(PARALLEL_JOBS)) \ + --epoch="$$(git show -s --format='%ci')" \ + --options='$(ANDROID_ASSETS_COMPRESSION)' \ + $(abspath $(ANDROID_ASSETS)/module/koreader.7z) \ + . '-x!libs' '-x!sdcv' $(release_excludes) # Note: we filter out the `--debug=…` make flag so the old # crummy version provided by the NDK does not blow a gasket. env \ diff --git a/make/appimage.mk b/make/appimage.mk index 39b4ccd2ff39..cca4e557e270 100644 --- a/make/appimage.mk +++ b/make/appimage.mk @@ -1,31 +1,36 @@ APPIMAGE_DIR = $(PLATFORM_DIR)/appimage APPIMAGETOOL = appimagetool-x86_64.AppImage -APPIMAGETOOL_URL = https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-x86_64.AppImage +APPIMAGETOOL_URL = https://github.com/AppImage/AppImageKit/releases/download/13/$(APPIMAGETOOL) + +KOREADER_APPIMAGE = koreader-$(DIST)-$(MACHINE)-$(VERSION).AppImage + +UBUNTU_LIBBSD = /lib/x86_64-linux-gnu/libbsd.so.0 + +define UPDATE_PATH_EXCLUDES += +plugins/SSH.koplugin +plugins/autofrontlight.koplugin +plugins/timesync.koplugin +$(filter-out tools/trace_require.lua tools/wbuilder.lua,$(wildcard tools/*)) +endef update: all - # remove old package if any - rm -f koreader-appimage-$(MACHINE)-$(VERSION).appimage - $(SYMLINK) $(APPIMAGE_DIR)/AppRun $(INSTALL_DIR)/koreader/ - $(SYMLINK) $(APPIMAGE_DIR)/koreader.desktop $(INSTALL_DIR)/koreader/ - $(SYMLINK) resources/koreader.png $(INSTALL_DIR)/koreader/ - sed -e 's/%%VERSION%%/$(VERSION)/' -e 's/%%DATE%%/$(RELEASE_DATE)/' $(PLATFORM_DIR)/common/koreader.metainfo.xml >$(INSTALL_DIR)/koreader/koreader.appdata.xml - # also copy libbsd.so.0, cf. https://github.com/koreader/koreader/issues/4627 - $(SYMLINK) /lib/x86_64-linux-gnu/libbsd.so.0 $(INSTALL_DIR)/koreader/libs/ -ifeq ("$(wildcard $(APPIMAGETOOL))","") - # download appimagetool - wget "$(APPIMAGETOOL_URL)" - chmod a+x "$(APPIMAGETOOL)" + cd $(INSTALL_DIR)/koreader && '$(abspath tools/mkrelease.sh)' ../appimage/ . $(release_excludes) + cp $(APPIMAGE_DIR)/{AppRun,koreader.desktop} resources/koreader.png $(INSTALL_DIR)/appimage/ + sed -e 's/%%VERSION%%/$(VERSION)/' -e 's/%%DATE%%/$(RELEASE_DATE)/' $(PLATFORM_DIR)/common/koreader.metainfo.xml >$(INSTALL_DIR)/appimage/koreader.appdata.xml + # Also copy libbsd.so.0 (cf. https://github.com/koreader/koreader/issues/4627). +ifeq (,$(wildcard $(UBUNTU_LIBBSD))) + # Only warn if it's missing (e.g. when testing on a non-Ubuntu distribution). + echo 'WARNING: not bundling missing $(UBUNTU_LIBBSD)' 1>&2 +else + cp $(UBUNTU_LIBBSD) $(INSTALL_DIR)/appimage/libs/ +endif +ifeq (,$(wildcard $(APPIMAGETOOL))) + # Download appimagetool. + wget '$(APPIMAGETOOL_URL)' + chmod a+x ./$(APPIMAGETOOL) endif - cd $(INSTALL_DIR) && pwd && \ - rm -rf tmp && mkdir -p tmp && \ - cp -Lr koreader tmp && \ - rm -rf tmp/koreader/ota && \ - rm -rf tmp/koreader/resources/icons/src && \ - rm -rf tmp/koreader/spec - # generate AppImage - cd $(INSTALL_DIR)/tmp && \ - ARCH=x86_64 "$$OLDPWD/$(APPIMAGETOOL)" --appimage-extract-and-run koreader && \ - mv *.AppImage ../../koreader-$(DIST)-$(MACHINE)-$(VERSION).AppImage + # Generate AppImage. + ARCH=x86_64 ./$(APPIMAGETOOL) --appimage-extract-and-run $(INSTALL_DIR)/appimage $(KOREADER_APPIMAGE) PHONY += update diff --git a/make/cervantes.mk b/make/cervantes.mk index fbf93db7209f..eb2cb898d008 100644 --- a/make/cervantes.mk +++ b/make/cervantes.mk @@ -2,32 +2,21 @@ CERVANTES_DIR = $(PLATFORM_DIR)/cervantes CERVANTES_PACKAGE = koreader-cervantes$(KODEDUG_SUFFIX)-$(VERSION).zip CERVANTES_PACKAGE_OTA = koreader-cervantes$(KODEDUG_SUFFIX)-$(VERSION).targz +define UPDATE_PATH_EXCLUDES += +tools +endef + update: all # ensure that the binaries were built for ARM file --dereference $(INSTALL_DIR)/koreader/luajit | grep ARM # remove old package if any rm -f $(CERVANTES_PACKAGE) # Cervantes launching scripts - cp $(COMMON_DIR)/spinning_zsync $(INSTALL_DIR)/koreader/spinning_zsync.sh - cp $(CERVANTES_DIR)/*.sh $(INSTALL_DIR)/koreader - cp $(CERVANTES_DIR)/spinning_zsync $(INSTALL_DIR)/koreader - # create new package - cd $(INSTALL_DIR) && \ - zip -9 -r \ - ../$(CERVANTES_PACKAGE) \ - koreader -x "koreader/resources/fonts/*" \ - "koreader/resources/icons/src/*" "koreader/spec/*" \ - $(ZIP_EXCLUDE) - # generate update package index file - zipinfo -1 $(CERVANTES_PACKAGE) > \ - $(INSTALL_DIR)/koreader/ota/package.index - echo "koreader/ota/package.index" >> $(INSTALL_DIR)/koreader/ota/package.index - # update index file in zip package - cd $(INSTALL_DIR) && zip -u ../$(CERVANTES_PACKAGE) \ - koreader/ota/package.index - # make gzip cervantes update for zsync OTA update - cd $(INSTALL_DIR) && \ - tar --hard-dereference -I"gzip --rsyncable" -cah --no-recursion -f ../$(CERVANTES_PACKAGE_OTA) \ - -T koreader/ota/package.index + $(SYMLINK) $(COMMON_DIR)/spinning_zsync $(INSTALL_DIR)/koreader/spinning_zsync.sh + $(SYMLINK) $(CERVANTES_DIR)/*.sh $(INSTALL_DIR)/koreader + $(SYMLINK) $(CERVANTES_DIR)/spinning_zsync $(INSTALL_DIR)/koreader + # Create packages. + $(strip $(call mkupdate,$(CERVANTES_PACKAGE))) + $(strip $(call mkupdate,$(CERVANTES_PACKAGE_OTA))) PHONY += update diff --git a/make/kindle.mk b/make/kindle.mk index a73f886ef3c0..d796002160b5 100644 --- a/make/kindle.mk +++ b/make/kindle.mk @@ -1,18 +1,20 @@ KINDLE_DIR = $(PLATFORM_DIR)/kindle KINDLE_PACKAGE = koreader-$(DIST)$(KODEDUG_SUFFIX)-$(VERSION).zip +# Note: the targz extension is intended to keep ISP from caching the file (Cf. koreader#1644). KINDLE_PACKAGE_OTA = koreader-$(DIST)$(KODEDUG_SUFFIX)-$(VERSION).targz -ZIP_EXCLUDE = -x "*.swp" -x "*.swo" -x "*.orig" -x "*.un~" # Don't bundle launchpad on touch devices.. ifeq ($(TARGET), kindle-legacy) KINDLE_LEGACY_LAUNCHER = launchpad endif +define UPDATE_PATH_EXCLUDES += +tools +endef + update: all # ensure that the binaries were built for ARM file --dereference $(INSTALL_DIR)/koreader/luajit | grep ARM - # remove old package if any - rm -f $(KINDLE_PACKAGE) # Kindle launching scripts $(SYMLINK) $(KINDLE_DIR)/extensions $(INSTALL_DIR)/ $(SYMLINK) $(KINDLE_DIR)/launchpad $(INSTALL_DIR)/ @@ -21,26 +23,8 @@ update: all $(SYMLINK) $(KINDLE_DIR)/libkohelper.sh $(INSTALL_DIR)/extensions/koreader/bin/ $(SYMLINK) $(COMMON_DIR)/spinning_zsync $(INSTALL_DIR)/koreader/ $(SYMLINK) $(KINDLE_DIR)/wmctrl $(INSTALL_DIR)/koreader/ - # create new package - cd $(INSTALL_DIR) && pwd && \ - zip -9 -r \ - ../$(KINDLE_PACKAGE) \ - extensions koreader $(KINDLE_LEGACY_LAUNCHER) \ - -x "koreader/resources/fonts/*" "koreader/ota/*" \ - "koreader/resources/icons/src/*" "koreader/spec/*" \ - $(ZIP_EXCLUDE) - # generate kindleupdate package index file - zipinfo -1 $(KINDLE_PACKAGE) > \ - $(INSTALL_DIR)/koreader/ota/package.index - echo "koreader/ota/package.index" >> $(INSTALL_DIR)/koreader/ota/package.index - # update index file in zip package - cd $(INSTALL_DIR) && zip -u ../$(KINDLE_PACKAGE) \ - koreader/ota/package.index - # make gzip kindleupdate for zsync OTA update - # note that the targz file extension is intended to keep ISP from caching - # the file, see koreader#1644. - cd $(INSTALL_DIR) && \ - tar --hard-dereference -I"gzip --rsyncable" -cah --no-recursion -f ../$(KINDLE_PACKAGE_OTA) \ - -T koreader/ota/package.index + # Create packages. + $(strip $(call mkupdate,$(KINDLE_PACKAGE))) extensions $(KINDLE_LEGACY_LAUNCHER) + $(strip $(call mkupdate,$(KINDLE_PACKAGE_OTA))) extensions $(KINDLE_LEGACY_LAUNCHER) PHONY += update diff --git a/make/kobo.mk b/make/kobo.mk index 2bce8f0f144b..36764fd01d4b 100644 --- a/make/kobo.mk +++ b/make/kobo.mk @@ -2,32 +2,19 @@ KOBO_DIR = $(PLATFORM_DIR)/kobo KOBO_PACKAGE = koreader-$(DIST)$(KODEDUG_SUFFIX)-$(VERSION).zip KOBO_PACKAGE_OTA = koreader-$(DIST)$(KODEDUG_SUFFIX)-$(VERSION).targz +define UPDATE_PATH_EXCLUDES += +$(filter-out tools/kobo%,$(wildcard tools/*)) +endef + update: all # ensure that the binaries were built for ARM file --dereference $(INSTALL_DIR)/koreader/luajit | grep ARM - # remove old package if any - rm -f $(KOBO_PACKAGE) # Kobo launching scripts - cp $(KOBO_DIR)/koreader.png $(INSTALL_DIR)/koreader.png - cp $(KOBO_DIR)/*.sh $(INSTALL_DIR)/koreader - cp $(COMMON_DIR)/spinning_zsync $(INSTALL_DIR)/koreader - # create new package - cd $(INSTALL_DIR) && \ - zip -9 -r \ - ../$(KOBO_PACKAGE) \ - koreader -x "koreader/resources/fonts/*" \ - "koreader/resources/icons/src/*" "koreader/spec/*" \ - $(ZIP_EXCLUDE) - # generate koboupdate package index file - zipinfo -1 $(KOBO_PACKAGE) > \ - $(INSTALL_DIR)/koreader/ota/package.index - echo "koreader/ota/package.index" >> $(INSTALL_DIR)/koreader/ota/package.index - # update index file in zip package - cd $(INSTALL_DIR) && zip -u ../$(KOBO_PACKAGE) \ - koreader/ota/package.index koreader.png README_kobo.txt - # make gzip koboupdate for zsync OTA update - cd $(INSTALL_DIR) && \ - tar --hard-dereference -I"gzip --rsyncable" -cah --no-recursion -f ../$(KOBO_PACKAGE_OTA) \ - -T koreader/ota/package.index + $(SYMLINK) $(KOBO_DIR)/koreader.png $(INSTALL_DIR)/ + $(SYMLINK) $(KOBO_DIR)/*.sh $(INSTALL_DIR)/koreader/ + $(SYMLINK) $(COMMON_DIR)/spinning_zsync $(INSTALL_DIR)/koreader/ + # Create packages. + $(strip $(call mkupdate,--manifest-transform=/^koreader\.png$$/d $(KOBO_PACKAGE))) koreader.png + $(strip $(call mkupdate,$(KOBO_PACKAGE_OTA))) PHONY += update diff --git a/make/linux.mk b/make/linux.mk index b00a6c2fe7dd..f45b79063d55 100644 --- a/make/linux.mk +++ b/make/linux.mk @@ -1,44 +1,36 @@ LINUX_DIR = $(PLATFORM_DIR)/linux -LINUX_PACKAGE:=koreader-linux-$(LINUX_ARCH_NAME)$(KODEDUG_SUFFIX)-$(VERSION).tar.xz +LINUX_PACKAGE = koreader-linux-$(LINUX_ARCH_NAME)$(KODEDUG_SUFFIX)-$(VERSION).tar.xz +LINUX_PACKAGE_COMPRESSION_LEVEL ?= 9 -GLIBC_VERSION := $(shell ldd --version | sed -n '1s/.* \([0-9.]\+\)$$/\1/p') +GLIBC_VERSION = $(shell ldd --version | sed -n '1s/.* \([0-9.]\+\)$$/\1/p') + +define UPDATE_PATH_EXCLUDES += +plugins/SSH.koplugin +plugins/autofrontlight.koplugin +plugins/timesync.koplugin +$(filter-out tools/trace_require.lua tools/wbuilder.lua,$(wildcard tools/*)) +endef update: all - mkdir -pv \ - $(INSTALL_DIR)/linux/bin \ - $(INSTALL_DIR)/linux/lib \ - $(INSTALL_DIR)/linux/share/pixmaps \ - $(INSTALL_DIR)/linux/share/metainfo \ - $(INSTALL_DIR)/linux/share/applications \ - $(INSTALL_DIR)/linux/share/doc/koreader \ - $(INSTALL_DIR)/linux/share/man/man1 + rm -rf $(INSTALL_DIR)/linux + mkdir -p $(INSTALL_DIR)/linux/{bin,lib,share/{applications,doc/koreader,man/man1,metainfo,pixmaps}} sed -e 's/%%VERSION%%/$(VERSION)/g' -e 's/%%DATE%%/$(RELEASE_DATE)/' $(PLATFORM_DIR)/common/koreader.metainfo.xml >$(INSTALL_DIR)/linux/share/metainfo/koreader.metainfo.xml - cp -pv resources/koreader.png $(INSTALL_DIR)/linux/share/pixmaps - cp -pv $(LINUX_DIR)/koreader.desktop $(INSTALL_DIR)/linux/share/applications - cp -pv $(LINUX_DIR)/copyright COPYING $(INSTALL_DIR)/linux/share/doc/koreader - cp -pv $(LINUX_DIR)/koreader.sh $(INSTALL_DIR)/linux/bin/koreader - cp -Lr $(INSTALL_DIR)/koreader $(INSTALL_DIR)/linux/lib - gzip -cn9 $(LINUX_DIR)/koreader.1 > $(INSTALL_DIR)/linux/share/man/man1/koreader.1.gz - chmod 644 \ - $(INSTALL_DIR)/linux/share/doc/koreader/copyright \ - $(INSTALL_DIR)/linux/share/man/man1/koreader.1.gz - rm -rf \ - $(INSTALL_DIR)/linux/lib/koreader/{ota,cache,clipboard,screenshots,spec,tools,resources/fonts,resources/icons/src} - - # remove leftovers - find $(INSTALL_DIR)/linux -type f \( -name ".git" -o -name ".gitignore" -o -name "discovery2spore" -o -name "wadl2spore" -o -name "*.txt" -o -name "LICENSE*" -o -name "NOTICE" -o -name "README.md" \) -print0 | xargs -0 rm -rf - find $(INSTALL_DIR)/linux -type d \( -name "test" -o -name ".github" \) -print0 | xargs -0 rm -rf - - # add instructions + $(SYMLINK) $(LINUX_DIR)/koreader.sh $(INSTALL_DIR)/linux/bin/koreader + $(SYMLINK) $(INSTALL_DIR)/koreader $(INSTALL_DIR)/linux/lib/ + $(SYMLINK) resources/koreader.png $(INSTALL_DIR)/linux/share/pixmaps/ + $(SYMLINK) $(LINUX_DIR)/koreader.desktop $(INSTALL_DIR)/linux/share/applications/ + $(SYMLINK) $(LINUX_DIR)/copyright COPYING $(INSTALL_DIR)/linux/share/doc/koreader/ + gzip -cn9 $(LINUX_DIR)/koreader.1 >$(INSTALL_DIR)/linux/share/man/man1/koreader.1.gz + # Add instructions. sed -e 's/%%VERSION%%/$(VERSION)/' \ -e 's/%%ARCH%%/$(LINUX_ARCH_NAME)/' \ -e 's/%%ABI%%/$(GLIBC_VERSION)/' \ $(LINUX_DIR)/instructions.txt >$(INSTALL_DIR)/linux/README.md - - # fix permissions - chmod -R u=rwX,og=rX $(INSTALL_DIR)/linux - XZ_OPT=9 tar -C $(INSTALL_DIR)/linux -cvJf $(LINUX_PACKAGE) . - - rm -rf $(INSTALL_DIR)/linux + # Create archive. + cd $(INSTALL_DIR)/linux && \ + '$(abspath tools/mkrelease.sh)' \ + $(if $(PARALLEL_JOBS),--jobs $(PARALLEL_JOBS)) \ + --options=-$(LINUX_PACKAGE_COMPRESSION_LEVEL) \ + '$(abspath $(LINUX_PACKAGE))' . $(call release_excludes,lib/koreader/) PHONY += update diff --git a/make/macos.mk b/make/macos.mk index f34bfca6055c..ee6c5fb9763e 100644 --- a/make/macos.mk +++ b/make/macos.mk @@ -1,14 +1,20 @@ MACOS_DIR = $(PLATFORM_DIR)/mac +define UPDATE_PATH_EXCLUDES += +plugins/SSH.koplugin +plugins/autofrontlight.koplugin +plugins/hello.koplugin +plugins/timesync.koplugin +tools +endef + update: all - mkdir -p \ - $(INSTALL_DIR)/bundle/Contents/MacOS \ - $(INSTALL_DIR)/bundle/Contents/Resources + mkdir -p $(INSTALL_DIR)/bundle/Contents/{MacOS,Resources} cp -pv $(MACOS_DIR)/koreader.icns $(INSTALL_DIR)/bundle/Contents/Resources/icon.icns - cp -LR $(INSTALL_DIR)/koreader $(INSTALL_DIR)/bundle/Contents - cp -pRv $(MACOS_DIR)/menu.xml $(INSTALL_DIR)/bundle/Contents/MainMenu.xib - ibtool --compile "$(INSTALL_DIR)/bundle/Contents/Resources/Base.lproj/MainMenu.nib" "$(INSTALL_DIR)/bundle/Contents/MainMenu.xib" - rm -rfv "$(INSTALL_DIR)/bundle/Contents/MainMenu.xib" + cd $(INSTALL_DIR)/koreader && '$(abspath tools/mkrelease.sh)' ../bundle/Contents/koreader/ . $(release_excludes) + cp -pv $(MACOS_DIR)/menu.xml $(INSTALL_DIR)/bundle/Contents/MainMenu.xib + ibtool --compile $(INSTALL_DIR)/bundle/Contents/Resources/Base.lproj/MainMenu.nib $(INSTALL_DIR)/bundle/Contents/MainMenu.xib + rm -vf $(INSTALL_DIR)/bundle/Contents/MainMenu.xib $(CURDIR)/platform/mac/do_mac_bundle.sh $(INSTALL_DIR) PHONY += update diff --git a/make/pocketbook.mk b/make/pocketbook.mk index 987288ff35b0..f84ed4e1e817 100644 --- a/make/pocketbook.mk +++ b/make/pocketbook.mk @@ -2,42 +2,23 @@ POCKETBOOK_DIR = $(PLATFORM_DIR)/pocketbook PB_PACKAGE = koreader-pocketbook$(KODEDUG_SUFFIX)-$(VERSION).zip PB_PACKAGE_OTA = koreader-pocketbook$(KODEDUG_SUFFIX)-$(VERSION).targz +define UPDATE_PATH_EXCLUDES += +tools +endef + update: all # ensure that the binaries were built for ARM file --dereference $(INSTALL_DIR)/koreader/luajit | grep ARM - # remove old package if any - rm -f $(PB_PACKAGE) # Pocketbook launching scripts + rm -rf $(INSTALL_DIR)/{applications,system} mkdir -p $(INSTALL_DIR)/applications mkdir -p $(INSTALL_DIR)/system/bin - cp $(POCKETBOOK_DIR)/koreader.app $(INSTALL_DIR)/applications - cp $(POCKETBOOK_DIR)/system_koreader.app $(INSTALL_DIR)/system/bin/koreader.app - cp $(COMMON_DIR)/spinning_zsync $(INSTALL_DIR)/koreader - cp -rfL $(INSTALL_DIR)/koreader $(INSTALL_DIR)/applications - find $(INSTALL_DIR)/applications/koreader \ - -type f \( -name "*.gif" -o -name "*.html" -o -name "*.md" -o -name "*.txt" \) \ - -exec rm -vf {} \; - # create new package - cd $(INSTALL_DIR) && \ - zip -9 -r \ - ../$(PB_PACKAGE) \ - applications -x "applications/koreader/resources/fonts/*" \ - "applications/koreader/resources/icons/src/*" "applications/koreader/spec/*" \ - $(ZIP_EXCLUDE) - # generate pocketbook package index file - zipinfo -1 $(PB_PACKAGE) > \ - $(INSTALL_DIR)/applications/koreader/ota/package.index - echo "applications/koreader/ota/package.index" >> \ - $(INSTALL_DIR)/applications/koreader/ota/package.index - # hack file path when running tar in parent directory of koreader - sed -i -e 's/^/..\//' \ - $(INSTALL_DIR)/applications/koreader/ota/package.index - # update index file in zip package - cd $(INSTALL_DIR) && zip -ru ../$(PB_PACKAGE) \ - applications/koreader/ota/package.index system - # make gzip pbupdate for zsync OTA update - cd $(INSTALL_DIR)/applications && \ - tar --hard-dereference -I"gzip --rsyncable" -cah --no-recursion -f ../../$(PB_PACKAGE_OTA) \ - -T koreader/ota/package.index + $(SYMLINK) $(POCKETBOOK_DIR)/koreader.app $(INSTALL_DIR)/applications/ + $(SYMLINK) $(POCKETBOOK_DIR)/system_koreader.app $(INSTALL_DIR)/system/bin/koreader.app + $(SYMLINK) $(COMMON_DIR)/spinning_zsync $(INSTALL_DIR)/koreader/ + $(SYMLINK) $(INSTALL_DIR)/koreader $(INSTALL_DIR)/applications/ + # Create packages. + $(strip $(call mkupdate,--manifest-transform=/^system/d;s/^/..\// $(PB_PACKAGE),applications/koreader)) applications system + $(strip $(call mkupdate,--manifest-transform=s/^/..\// $(PB_PACKAGE_OTA),applications/koreader)) applications PHONY += update diff --git a/make/remarkable.mk b/make/remarkable.mk index 76e09d2711bb..f85ad0a2072a 100644 --- a/make/remarkable.mk +++ b/make/remarkable.mk @@ -2,31 +2,19 @@ REMARKABLE_DIR = $(PLATFORM_DIR)/remarkable REMARKABLE_PACKAGE = koreader-remarkable$(KODEDUG_SUFFIX)-$(VERSION).zip REMARKABLE_PACKAGE_OTA = koreader-remarkable$(KODEDUG_SUFFIX)-$(VERSION).targz +define UPDATE_PATH_EXCLUDES += +plugins/SSH.koplugin +tools +endef + update: all # ensure that the binaries were built for ARM file --dereference $(INSTALL_DIR)/koreader/luajit | grep ARM - # remove old package if any - rm -f $(REMARKABLE_PACKAGE) # Remarkable scripts - cp $(REMARKABLE_DIR)/* $(INSTALL_DIR)/koreader - cp $(COMMON_DIR)/spinning_zsync $(INSTALL_DIR)/koreader - # create new package - cd $(INSTALL_DIR) && \ - zip -9 -r \ - ../$(REMARKABLE_PACKAGE) \ - koreader -x "koreader/resources/fonts/*" \ - "koreader/resources/icons/src/*" "koreader/spec/*" \ - $(ZIP_EXCLUDE) - # generate update package index file - zipinfo -1 $(REMARKABLE_PACKAGE) > \ - $(INSTALL_DIR)/koreader/ota/package.index - echo "koreader/ota/package.index" >> $(INSTALL_DIR)/koreader/ota/package.index - # update index file in zip package - cd $(INSTALL_DIR) && zip -u ../$(REMARKABLE_PACKAGE) \ - koreader/ota/package.index - # make gzip remarkable update for zsync OTA update - cd $(INSTALL_DIR) && \ - tar -I"gzip --rsyncable" -cah --no-recursion -f ../$(REMARKABLE_PACKAGE_OTA) \ - -T koreader/ota/package.index + $(SYMLINK) $(REMARKABLE_DIR)/* $(INSTALL_DIR)/koreader/ + $(SYMLINK) $(COMMON_DIR)/spinning_zsync $(INSTALL_DIR)/koreader/ + # Create packages. + $(strip $(call mkupdate,$(REMARKABLE_PACKAGE))) + $(strip $(call mkupdate,$(REMARKABLE_PACKAGE_OTA))) PHONY += update diff --git a/make/sony-prstux.mk b/make/sony-prstux.mk index 724e228d23b0..94b00cc54fc7 100644 --- a/make/sony-prstux.mk +++ b/make/sony-prstux.mk @@ -2,30 +2,18 @@ SONY_PRSTUX_DIR = $(PLATFORM_DIR)/sony-prstux SONY_PRSTUX_PACKAGE = koreader-sony-prstux$(KODEDUG_SUFFIX)-$(VERSION).zip SONY_PRSTUX_PACKAGE_OTA = koreader-sony-prstux$(KODEDUG_SUFFIX)-$(VERSION).targz +define UPDATE_PATH_EXCLUDES += +plugins/SSH.koplugin +tools +endef + update: all # ensure that the binaries were built for ARM file --dereference $(INSTALL_DIR)/koreader/luajit | grep ARM - # remove old package if any - rm -f $(SONY_PRSTUX_PACKAGE) # Sony PRSTUX launching scripts - cp $(SONY_PRSTUX_DIR)/*.sh $(INSTALL_DIR)/koreader - # create new package - cd $(INSTALL_DIR) && \ - zip -9 -r \ - ../$(SONY_PRSTUX_PACKAGE) \ - koreader -x "koreader/resources/fonts/*" \ - "koreader/resources/icons/src/*" "koreader/spec/*" \ - $(ZIP_EXCLUDE) - # generate update package index file - zipinfo -1 $(SONY_PRSTUX_PACKAGE) > \ - $(INSTALL_DIR)/koreader/ota/package.index - echo "koreader/ota/package.index" >> $(INSTALL_DIR)/koreader/ota/package.index - # update index file in zip package - cd $(INSTALL_DIR) && zip -u ../$(SONY_PRSTUX_PACKAGE) \ - koreader/ota/package.index - # make gzip sonyprstux update for zsync OTA update - cd $(INSTALL_DIR) && \ - tar --hard-dereference -I"gzip --rsyncable" -cah --no-recursion -f ../$(SONY_PRSTUX_PACKAGE_OTA) \ - -T koreader/ota/package.index + $(SYMLINK) $(SONY_PRSTUX_DIR)/*.sh $(INSTALL_DIR)/koreader + # Create packages. + $(strip $(call mkupdate,$(SONY_PRSTUX_PACKAGE))) + $(strip $(call mkupdate,$(SONY_PRSTUX_PACKAGE_OTA))) PHONY += update diff --git a/platform/mac/do_mac_bundle.sh b/platform/mac/do_mac_bundle.sh index 029d679f708a..92eb70a02f5f 100755 --- a/platform/mac/do_mac_bundle.sh +++ b/platform/mac/do_mac_bundle.sh @@ -131,11 +131,6 @@ pushd "${APP_PATH}/Contents/koreader" lipo /usr/bin/tar -extract_family "${APP_ARCH}" -output tar mv COPYING README.md ../Resources/ mv koreader ../MacOS/koreader -rm -rf cache clipboard history ota \ - l10n/.git l10n/.tx l10n/templates l10n/LICENSE l10n/Makefile l10n/README.md \ - plugins/SSH.koplugin plugins/hello.koplugin plugins/timesync.koplugin \ - plugins/autofrontlight.koplugin resources/fonts resources/icons/src \ - rocks/bin rocks/lib/luarocks screenshots spec tools # Adjust reader.lua a bit. sed '1d' reader.lua >tempfile @@ -162,6 +157,6 @@ codesign --force --deep -s - "${APP_BUNDLE}.app" # Package as 7z reduces size from 80MB to 30MB. if command_exists "7z"; then - 7z a -l -m0=lzma2 -mx=9 "${APP_BUNDLE}-${APP_ARCH}-${VERSION}.7z" "${APP_BUNDLE}.app" - rm -rfv "${APP_BUNDLE}.app" + 7z a -l -m0=flzma2 -mx=9 "${APP_BUNDLE}-${APP_ARCH}-${VERSION}.7z" "${APP_BUNDLE}.app" + rm -rf "${APP_BUNDLE}.app" fi diff --git a/tools/mk7z.sh b/tools/mk7z.sh deleted file mode 100755 index 77584cfb9758..000000000000 --- a/tools/mk7z.sh +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env bash - -set -eo pipefail -# set -x - -[[ $# -ge 3 ]] -archive="$(realpath "$1")" -epoch="$2" -shift 2 -options=() -for a in "$@"; do - shift - case "${a}" in - --) break ;; - -*) options+=("${a}") ;; - *) break ;; - esac -done -[[ $# -gt 0 ]] -patterns=("$@") - -# We need to use the full path to the executable to avoid -# a weird issue when using the p7zip project pre-built -# binary (`Can't load './7z.dll' (7z.so)...`). -sevenzip="$(which 7z)" - -# echo "archive : ${archive}" -# echo "epoch : ${epoch}" -# echo "options : ${options[@]}" -# echo "patterns: ${patterns[@]}" - -tmpdir="$(mktemp -d -t tmp7z.XXXXXXXXXX)" -trap 'rm -rf "${tmpdir}"' EXIT - -manifest="${tmpdir}/manifest" - -# Detect if that version of 7z deferences symlinks by default. -sevenzip_manifest_cmd=("${sevenzip}" -ba h) -ln -s /dev/null "${tmpdir}/symlink" -checksum="$("${sevenzip_manifest_cmd[@]}" "${tmpdir}/symlink" | awk '{ print $1 }')" -if [[ "${checksum}" != '00000000' ]]; then - sevenzip_manifest_cmd+=(-l) -fi -rm -f "${tmpdir}/symlink" - -# Note: remove trailing `/` appended to directories in some 7z versions. -"${sevenzip_manifest_cmd[@]}" "${patterns[@]}" | - awk '{ if ($3!="") print $3, $2, $1; else { gsub("/$", "", $1); print $1 } }' | - sort >"${manifest}" - -# cat "${manifest}" | less - -if [[ -r "${archive}" ]]; then - if diff --brief --label 'in archive' \ - <( - "${sevenzip}" -slt l "${archive}" | - awk ' - /^([^=]+) = / { entry[$1] = $3; } - /^CRC =/ { if ($3!="") print entry["Path"], entry["Size"], $3; else print entry["Path"] } - ' | sort - ) --label 'to add' "${manifest}"; then - exit - fi - # There's no 7z option to overwrite the archive - # if it already exists (instead of updating it)… - rm -f "${archive}" -fi - -# Extract list of paths from manifest. -rev <"${manifest}" | cut -f3- -d' ' | rev >"${tmpdir}/paths" -# Quick sanity check: no path outside the current directory. -if grep '^(/|\.\./)' <"${tmpdir}/paths"; then - echo "^ some paths are outside the current directory!" - exit 1 -fi - -# Make a copy of everything so we can later -# patch timestamp to ensure reproducibility. -mkdir "${tmpdir}/contents" -# We want to copy "empty" (with ignored files) directories. -tar --dereference --no-recursion --create \ - --verbatim-files-from --files-from="${tmpdir}/paths" | - tar --extract --directory="${tmpdir}/contents" - -cd "${tmpdir}/contents" - -# Fix timestamps. -find . -depth -print0 | xargs -0 touch --date="${epoch}" - -# And create the final archive. -"${sevenzip}" -mqs "${options[@]}" a "${archive}" . - -# vim: sw=4 diff --git a/tools/mkrelease.sh b/tools/mkrelease.sh new file mode 100755 index 000000000000..66b10348a7b9 --- /dev/null +++ b/tools/mkrelease.sh @@ -0,0 +1,317 @@ +#!/usr/bin/env bash + +set -eo pipefail +# set -x + +declare -r USAGE=" +USAGE: + $0 [OPTIONS] [--] ARCHIVE_NAME|DIRECTORY/ [PATTERNS] + +OPTIONS: + -j N, --jobs N use up to N processes / threads + -e EPOCH, --epoch EPOCH set contents timestamp to EPOCH + -m ENTRY, --manifest ENTRY add a manifest ENTRY to the release + --manifest-transform SCRIPT transform manifest using sed SCRIPT + -o OPTS, --options OPTS forward options to compressor +" + +# shellcheck disable=SC2016 +declare -r AWK_HELPERS=' +function print_entry(path, size, crc) { + sub("/$", "", path) + if (crc) + print path, size, crc + else + print path"/" +} +function reverse_entry() { + $0 = $3" "$2" "$1; +} +' + +if [[ "${OSTYPE}" = darwin* ]]; then + declare -r READLINK=greadlink + declare -r TAR=gtar +else + declare -r READLINK=readlink + declare -r TAR=tar +fi + +if ! opt=$(getopt -o '+de:hj:m:o:' --long 'debug,epoch:,help,jobs:,manifest:,manifest-transform:,options:' --name "$0" -- "$@"); then + echo "${USAGE}" + exit 1 +fi + +# Arguments parsing. {{{ + +debug='' +epoch='' +jobs='' +manifest='' +manifest_transform='' +options=() + +eval set -- "${opt}" +while [[ $# -gt 0 ]]; do + case "$1" in + -d | --debug) + debug=1 + ;; + -e | --epoch) + epoch="$2" + shift + ;; + -h | --help) + echo "${USAGE}" + exit 0 + ;; + -j | --jobs) + jobs="$2" + shift + ;; + -m | --manifest) + manifest="$2" + shift + ;; + --manifest-transform) + manifest_transform="$2" + shift + ;; + -o | --options) + declare -a "a=($2)" + options+=("${a[@]}") + shift + ;; + --) + shift + break + ;; + esac + shift +done + +if [[ $# -lt 1 ]]; then + echo "${USAGE}" + exit 1 +fi + +case "$1" in + */) format='/' ;; + *.7z | *.zip) format="${1##*.}" ;; + *.tar.gz | *.targz) format=tar.gz ;; + *.tar.xz) format=tar.xz ;; + *.tar.zstd) format=tar.zstd ;; + *) + echo "ERROR: unsupported release format: ${1##*.}" 1>&2 + exit 2 + ;; +esac +output="$("${READLINK}" -f "$1")" +shift +patterns=("$@") +if [[ -n "${manifest}" ]]; then + patterns+=("-x!${manifest}") +fi + +# }}} + +if command -v pv >/dev/null; then + write_to_file() { + pv --interval=0.25 --bytes --timer --rate --output="$1" + } +else + write_to_file() { + dd bs=4096 of="$1" + } +fi + +# Ensure a "traditional" sort order. +export LC_ALL=C + +# We need to use the full path to the executable to avoid +# a weird issue when using the p7zip project pre-built +# binary (`Can't load './7z.dll' (7z.so)...`). +if ! sevenzip="$(which 7z)"; then + echo "ERROR: 7z executable not found!" 1>&2 + exit 2 +fi +sevenzip_compress_cmd=("${sevenzip}") + +# Setup temporary directory. +tmpdir="$(mktemp -d -t tmp7z.XXXXXXXXXX)" +trap 'rm -rf "${tmpdir}"' EXIT +mkdir -p "${tmpdir}/contents" + +# Detect if that version of 7z deferences symlinks by default. +sevenzip_manifest_cmd=("${sevenzip}" -ba h) +ln -s /dev/null "${tmpdir}/symlink" +checksum="$("${sevenzip_manifest_cmd[@]}" "${tmpdir}/symlink" | awk '{ print $1 }')" +if [[ "${checksum}" != '00000000' ]]; then + sevenzip_manifest_cmd+=(-l) +fi +rm -f "${tmpdir}/symlink" + +# Prefer `pigz` over `gzip` (faster). +gzip="$(command -v pigz || command -v gzip)" +gzip_cmd=("${gzip}") + +# Jobs. +if [[ -n "${jobs}" ]]; then + [[ "${gzip}" != */pigz ]] || gzip_cmd+=(--processes "${jobs}") + sevenzip_compress_cmd+=(-mmt="${jobs}") +fi + +# In-place sed command. +if [[ "${OSTYPE}" = darwin* ]]; then + ised=(sed -i '' -e) +else + ised=(sed -i -e) +fi + +if [[ -n "${debug}" ]]; then + echo "format : ${format}" + echo "output : ${output}" + echo "manifest : ${manifest}" + echo "manifest transform : ${manifest_transform}" + echo "epoch : ${epoch}" + echo "options : ${options[*]@Q}" + echo "patterns : ${patterns[*]@Q}" + echo "7z executable : ${sevenzip}" + echo "7z compress command : ${sevenzip_compress_cmd[*]@Q}" + echo "7z manifest command : ${sevenzip_manifest_cmd[*]@Q}" + echo "gzip executable : ${gzip}" + echo "gzip command : ${gzip_cmd[*]@Q}" + echo "sed in-place command: ${ised[*]@Q}" + [[ -t 0 ]] && read -srn 1 +fi + +# Build manifest. +"${sevenzip_manifest_cmd[@]}" "${patterns[@]}" | + awk "${AWK_HELPERS}"'{ reverse_entry(); print_entry($1, $2, $3) }' | + sort -o "${tmpdir}/manifest" + +# Extract list of paths from manifest. +rev <"${tmpdir}/manifest" | cut -f3- -d' ' | rev >"${tmpdir}/paths" +# Quick sanity check: no path outside the current directory. +if grep '^(/|\.\./)' <"${tmpdir}/paths"; then + echo "ERROR: ^ some paths are outside the current directory!" + exit 2 +fi + +# Don't forget the archive's internal manifest, if requested. +if [[ -n "${manifest}" ]]; then + manifest_path=("${manifest}") + while [[ "${manifest_path[0]}" = */*[^/] ]]; do + manifest_path=("${manifest_path[0]%/*}/" "${manifest_path[@]}") + done + { + cat "${tmpdir}/paths" + printf '%s\n' "${manifest_path[@]}" + } | sort -u -o "${tmpdir}/paths_with_manifest" + install --mode=0644 -D "${tmpdir}/paths_with_manifest" "${tmpdir}/contents/${manifest}" + if [[ -n "${manifest_transform}" ]]; then + "${ised[@]}" "${manifest_transform}" "${tmpdir}/contents/${manifest}" + fi + # Can't use 7z's `-w` option in this case… + pushd "${tmpdir}/contents" >/dev/null + "${sevenzip[@]}" -ba h | + awk "${AWK_HELPERS}"'{ reverse_entry(); print_entry($1, $2, $3) }' >>"${tmpdir}/manifest" + popd >/dev/null + sort -u -o "${tmpdir}/manifest" "${tmpdir}/manifest" +fi + +if [[ -n "${debug}" ]] && [[ -t 0 ]]; then + paths=("${tmpdir}/manifest" "${tmpdir}"/paths*) + [[ -z "${manifest}" ]] || paths+=("${tmpdir}/contents/${manifest}") + less "${paths[@]}" +fi + +# If the output already exists, check for changes. +if [[ -r "${output}" ]]; then + previous_manifest='' + case "${format}" in + /) ;; + tar*) ;; + *) + previous_manifest="$( + "${sevenzip[@]}" -ba -slt l "${output}" | + awk "${AWK_HELPERS}"' + /^[^=]+ = / { e[$1] = $3; } + /^$/ && e["Size"] != "" { print_entry(e["Path"], e["Size"], e["CRC"]) } + ' | sort + )" + ;; + esac + if [[ -n "${previous_manifest}" ]] && + diff --color${CLICOLOR_FORCE:+=always} --unified \ + --label 'old' <(printf '%s\n' "${previous_manifest}") \ + --label 'new' "${tmpdir}/manifest"; then + exit + fi + # There's no 7z or zip option to overwrite the output + # if it already exists (instead of updating it), and + # it may be a directory anyway… + rm -rf "${output}" +fi + +# Make a copy of everything so we can later patch timestamps and +# fix permissions to ensure reproducibility. +# Note: We want to keep "empty" (with ignored files) directories. +"${TAR}" --create --dereference --hard-dereference --no-recursion \ + --verbatim-files-from --files-from="${tmpdir}/paths" | + "${TAR}" --extract --directory="${tmpdir}/contents" + +cd "${tmpdir}/contents" + +# Fix permissions. +chmod -R u=rwX,og=rX . + +# Fix timestamps. +if [[ -n "${epoch}" ]]; then + find . -depth -print0 | xargs -0 touch --date="${epoch}" +fi + +# And create the final output. +if [[ -n "${manifest}" ]]; then + filelist="${tmpdir}/paths_with_manifest" +else + filelist="${tmpdir}/paths" +fi +sevenzip_compress_cmd+=("${options[@]}" a "${output}" "-i@${filelist}") +tar_compress_cmd=( + "${TAR}" --create --no-recursion --owner=0 --group=0 + --verbatim-files-from --files-from="${filelist}" +) +case "${format}" in + /) + mv "${tmpdir}/contents" "${output}" + ;; + 7z) + # Note: sort by type (for better compression). + "${sevenzip_compress_cmd[@]}" -mqs + ;; + tar.gz) + echo "Creating archive: ${output}" + # Note: create a rsyncable gzipped tar. + "${tar_compress_cmd[@]}" | + "${gzip}" --no-name --rsyncable "${options[@]}" --stdout | + write_to_file "${output}" + ;; + tar.xz) + echo "Creating archive: ${output}" + "${tar_compress_cmd[@]}" | + xz ${jobs:+--threads=${jobs}} "${options[@]}" | + write_to_file "${output}" + ;; + tar.zstd) + echo "Creating archive: ${output}" + "${tar_compress_cmd[@]}" | + zstd ${jobs:+--threads=${jobs}} "${options[@]}" --stdout | + write_to_file "${output}" + ;; + zip) + "${sevenzip_compress_cmd[@]}" -tzip + ;; +esac + +# vim: foldmethod=marker foldlevel=0 sw=4