├── appimage ├── image │ ├── VERSION │ ├── appimagetool.sh │ ├── Makefile │ ├── build.sh │ └── Dockerfile ├── templates │ ├── AppRun │ ├── app.desktop.template │ ├── app.metainfo.xml │ └── app.svg ├── README.md ├── scripts │ └── bundler.lisp └── appimage.lisp ├── .distignore ├── msix ├── templates │ ├── app44.png │ ├── app150.png │ └── appxmanifest.xml ├── image │ ├── make.ps1 │ ├── resources │ │ ├── entrypoint.ps1 │ │ ├── provision.ps1 │ │ ├── init.ps1 │ │ ├── init.lisp │ │ ├── sign.ps1 │ │ └── build.ps1 │ └── Dockerfile ├── README.md ├── scripts │ └── bundler.lisp └── msix.lisp ├── archive ├── README.md ├── scripts │ └── bundler.lisp └── archive.lisp ├── delivery ├── scripts │ ├── registry.lisp │ ├── blobs.lisp │ ├── deliver.lisp │ ├── builder.lisp │ └── build.lisp ├── packages.lisp ├── registry.lisp ├── bundle.lisp └── delivery.lisp ├── LICENSE ├── README.md ├── alien-works-delivery.asd └── util └── uti.lisp /appimage/image/VERSION: -------------------------------------------------------------------------------- 1 | 1.0.0 -------------------------------------------------------------------------------- /.distignore: -------------------------------------------------------------------------------- 1 | /\..* # any file or directory starting with dot 2 | -------------------------------------------------------------------------------- /appimage/templates/AppRun: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export ALIEN_WORKS_WORKDIR=$APPDIR 3 | exec $APPDIR/usr/bin/app $@ -------------------------------------------------------------------------------- /appimage/image/appimagetool.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exec appimagetool.AppImage --appimage-extract-and-run $@ 4 | -------------------------------------------------------------------------------- /msix/templates/app44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borodust/alien-works-delivery/HEAD/msix/templates/app44.png -------------------------------------------------------------------------------- /msix/templates/app150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/borodust/alien-works-delivery/HEAD/msix/templates/app150.png -------------------------------------------------------------------------------- /msix/image/make.ps1: -------------------------------------------------------------------------------- 1 | docker build -t registry.awie.club/alien-works-delivery-msix -f $PSScriptRoot/Dockerfile $PSScriptRoot 2 | -------------------------------------------------------------------------------- /appimage/templates/app.desktop.template: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=alien-works-demo 3 | Type=Application 4 | Categories=Game; 5 | Exec=app 6 | Icon=app 7 | -------------------------------------------------------------------------------- /archive/README.md: -------------------------------------------------------------------------------- 1 | Required tools to make archive bundle from delivery one: 2 | * Linux-based 2018+ / Windows 10+ x64 OS 3 | * CL implementation: CCL, SBCL, ECL 4 | -------------------------------------------------------------------------------- /appimage/README.md: -------------------------------------------------------------------------------- 1 | Required tools to make AppImage bundle from delivery one: 2 | * GNU/Linux OS (usual stuff: sh, tar, cp, etc) 3 | * [appimagetool](https://github.com/AppImage/AppImageKit/releases) 4 | * CL implementation: CCL, SBCL, ECL 5 | -------------------------------------------------------------------------------- /msix/README.md: -------------------------------------------------------------------------------- 1 | Required tools to make MSIX bundle from delivery one: 2 | * Windows 10+ x64 OS (PowerShell) 3 | * [MakeAppx](https://github.com/microsoft/msix-packaging) 4 | * CL implementation: CCL, SBCL, ECL 5 | 6 | 7 | ```sh 8 | SignTool sign /fd SHA256 /v /sm /s My /n Alien-Works .msix 9 | ``` -------------------------------------------------------------------------------- /appimage/image/Makefile: -------------------------------------------------------------------------------- 1 | WORK_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) 2 | 3 | IMAGE_VERSION := $(shell cat ${WORK_DIR}/VERSION) 4 | TAG := registry.awie.club/awd-appimage-builder:$(IMAGE_VERSION) 5 | 6 | .PHONY: image push 7 | 8 | image: 9 | docker build -t $(TAG) -f $(WORK_DIR)/Dockerfile . 10 | 11 | push: 12 | docker push $(TAG) 13 | -------------------------------------------------------------------------------- /delivery/scripts/registry.lisp: -------------------------------------------------------------------------------- 1 | (cl:in-package :cl-user) 2 | 3 | ;; 4 | ;; INPUT: 5 | ;; *registry-paths* 6 | ;; 7 | 8 | (require 'asdf) 9 | (require 'uiop) 10 | (loop for relative-path in *registry-paths* 11 | do (push (merge-pathnames relative-path 12 | (uiop:pathname-directory-pathname *load-pathname*)) 13 | asdf:*central-registry*)) 14 | -------------------------------------------------------------------------------- /msix/image/resources/entrypoint.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [Parameter(Mandatory)] 3 | [string]$Operation, 4 | 5 | [Parameter(mandatory=$false, 6 | ValueFromRemainingArguments=$true)] 7 | $Remaining 8 | ) 9 | 10 | switch ( $Operation ) { 11 | 'build' { ./build.ps1 @Remaining } 12 | 'sign' { ./sign.ps1 @Remaining } 13 | default { 14 | echo "Unexpected operation: '$Operation'" 15 | exit 1 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /appimage/image/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BUNDLE_FILE=$1 4 | shift 5 | 6 | BUNDLE_DIR=`mktemp -d` 7 | 8 | echo "Extracting bundle archive into $BUNDLE_DIR" 9 | 10 | # deletes the temp directory 11 | function cleanup { 12 | rm -rf "$BUNDLE_DIR" 13 | echo "Deleted temp working directory $BUNDLE_DIR" 14 | } 15 | 16 | # register the cleanup function to be called on the EXIT signal 17 | trap cleanup EXIT 18 | 19 | cd $BUNDLE_DIR && tar -xf $BUNDLE_FILE 20 | cd $BUNDLE_DIR/delivery-bundle/ 21 | 22 | BUNDLE_SCRIPT=$BUNDLE_DIR/delivery-bundle/deliver.lisp 23 | exec sbcl --script $BUNDLE_SCRIPT $@ 24 | -------------------------------------------------------------------------------- /msix/image/resources/provision.ps1: -------------------------------------------------------------------------------- 1 | ### 2 | ### SBCL 3 | ### 4 | Invoke-WebRequest -UseBasicParsing -Uri https://github.com/roswell/sbcl_bin/releases/download/2.2.6/sbcl-2.2.6-x86-64-windows-binary.msi -OutFile c:/sbcl.msi 5 | 6 | $sbcl_install_args = "/i `"c:\sbcl.msi`" /quiet /passive" 7 | Start-Process msiexec.exe -ArgumentList $sbcl_install_args -Wait 8 | Remove-Item -Path C:/sbcl.msi 9 | 10 | $SBCL = "C:/Program Files/Steel Bank Common Lisp/sbcl.exe" 11 | 12 | ### 13 | ### QUICKLISP 14 | ### 15 | Invoke-WebRequest -UseBasicParsing -Uri https://beta.quicklisp.org/quicklisp.lisp -OutFile C:/quicklisp.lisp 16 | -------------------------------------------------------------------------------- /msix/image/resources/init.ps1: -------------------------------------------------------------------------------- 1 | # Add MakeAppx into path 2 | $PathRegKey = 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' 3 | $PersistentPath = (Get-ItemProperty -Path $PathRegKey -Name PATH).path 4 | $PersistentPath += ';C:\Program Files (x86)\Windows Kits\10\App Certification Kit\' 5 | Set-ItemProperty -Path $PathRegKey -Name PATH -Value $PersistentPath 6 | 7 | $PersistentPath = (Get-ItemProperty -Path $PathRegKey -Name PATH).path 8 | echo "New PATH: $PersistentPath" 9 | 10 | $SBCL = "C:/Program Files/Steel Bank Common Lisp/sbcl.exe" 11 | & $SBCL --script "$PSScriptRoot/init.lisp" 12 | -------------------------------------------------------------------------------- /appimage/image/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # MAIN 3 | # 4 | FROM clfoundation/sbcl:2.2.4-slim-bullseye 5 | 6 | COPY appimagetool.sh /usr/local/bin/appimagetool 7 | COPY build.sh /usr/local/bin/build.sh 8 | 9 | RUN apt-get update \ 10 | && apt-get -y install file curl fuse \ 11 | && curl -L https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage --output /usr/local/bin/appimagetool.AppImage \ 12 | && chmod +x /usr/local/bin/appimagetool.AppImage \ 13 | /usr/local/bin/appimagetool \ 14 | /usr/local/bin/build.sh \ 15 | && groupadd -g 1001 builder \ 16 | && useradd -m -u 1001 -g 1001 builder 17 | 18 | USER builder:builder 19 | 20 | ENTRYPOINT ["build.sh"] 21 | -------------------------------------------------------------------------------- /msix/image/resources/init.lisp: -------------------------------------------------------------------------------- 1 | (require 'asdf) 2 | 3 | (load (merge-pathnames "quicklisp.lisp" 4 | (uiop:pathname-directory-pathname *load-pathname*))) 5 | 6 | (quicklisp-quickstart:install 7 | :dist-url "http://dist.awie.club/awie/0/awie.txt") 8 | (ql-util:without-prompting 9 | (let ((init-file (ql:add-to-init-file))) 10 | (with-open-file (out init-file :direction :output 11 | :if-exists :append 12 | :element-type 'base-char 13 | :external-format :utf-8) 14 | (format out "~&#+quicklisp~%") 15 | (prin1 '(ql-util:without-prompting (ql:update-all-dists)) out)))) 16 | -------------------------------------------------------------------------------- /appimage/scripts/bundler.lisp: -------------------------------------------------------------------------------- 1 | (cl:in-package :cl-user) 2 | 3 | ;; 4 | ;; INPUT: 5 | ;; *bundler-output-filename* 6 | ;; 7 | (flet ((%system-path (relative) 8 | (merge-pathnames (uiop:relativize-pathname-directory relative) 9 | *target-bundle-directory*))) 10 | (cp *delivery-bundle-directory* (merge-pathnames "AppDir/" *bundle-directory*)) 11 | (let ((app-dir (merge-pathnames "AppDir/" *delivery-bundle-directory*))) 12 | (shell "chmod" "+x" (file app-dir "AppRun")) 13 | (shell "appimagetool" "--no-appstream" 14 | app-dir 15 | (or (provided-bundle-output-file) 16 | (merge-pathnames 17 | *bundler-output-filename* 18 | (%system-path "./")))))) 19 | -------------------------------------------------------------------------------- /appimage/templates/app.metainfo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | app 4 | 5 | alien-works-demo 6 | Demonstration project for alien-works 7 | 8 | CC0-1.0 9 | GPL-3.0-or-later 10 | 11 | 12 | pointing 13 | keyboard 14 | touch 15 | gamepad 16 | 17 | 18 | 19 |

20 | Project that demonstrates how alien-works work. This seems to be too short description. 21 |

22 |
23 | 24 | app.desktop 25 |
26 | -------------------------------------------------------------------------------- /msix/image/resources/sign.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [Parameter(Mandatory)] 3 | [string]$Target, 4 | 5 | [Parameter(Mandatory)] 6 | [string]$Cert, 7 | 8 | [Parameter(mandatory=$false, 9 | ValueFromRemainingArguments=$true)] 10 | $Remaining 11 | ) 12 | 13 | echo "Signing '$Target' with key '$Cert'" 14 | 15 | 16 | $ArgTable = @{} 17 | $Remaining | foreach { 18 | if($_ -match '^-') { 19 | $Key = ($_ -replace '^-').ToLower() 20 | } else { 21 | $ArgTable[$Key] = $_ 22 | } 23 | } 24 | 25 | 26 | $SignArgs = 'sign', '/fd', 'SHA256', '/v', '/f', "$Cert" 27 | 28 | $Password = $ArgTable['password'] 29 | if ( "$Password" -ne '' ) { 30 | echo "Password provided: $Password" 31 | $SignArgs += "/p", "$Password" 32 | } 33 | 34 | echo "Params: $SignArgs $Target" 35 | 36 | SignTool @SignArgs "$Target" 37 | -------------------------------------------------------------------------------- /msix/scripts/bundler.lisp: -------------------------------------------------------------------------------- 1 | (cl:in-package :cl-user) 2 | 3 | ;; 4 | ;; INPUT: 5 | ;; *bundler-output-filename* 6 | ;; 7 | (flet ((%system-path (relative) 8 | (merge-pathnames (uiop:relativize-pathname-directory relative) 9 | *target-bundle-directory*))) 10 | (cp *delivery-bundle-directory* (merge-pathnames "AppDir/" *bundle-directory*)) 11 | (let* ((app-dir (merge-pathnames "AppDir/" *delivery-bundle-directory*)) 12 | (target-bundle-file (or (provided-bundle-output-file) 13 | (merge-pathnames 14 | *bundler-output-filename* 15 | (%system-path "./"))))) 16 | (shout "Packing ~A" app-dir) 17 | (shell "MakeAppx" "pack" 18 | "/d" app-dir 19 | "/p" target-bundle-file) 20 | (shout "Bundle created at ~A." target-bundle-file))) 21 | -------------------------------------------------------------------------------- /delivery/packages.lisp: -------------------------------------------------------------------------------- 1 | (cl:defpackage :alien-works-delivery 2 | (:use :cl :alexandria :alien-works-delivery-util) 3 | (:export #:assemble-delivery-bundle 4 | 5 | #:*delivery-bundle-directory* 6 | #:*delivery-bundle-registry* 7 | #:*delivery-bundle-systems* 8 | #:make-delivery-bundle 9 | #:prepare-delivery-bundle 10 | #:delivery-bundle-foreign-library-directory 11 | #:delivery-bundle-asset-directory 12 | #:delivery-bundle-executable-path 13 | #:delivery-bundle-build-features 14 | #:delivery-bundle-assembler-parameters 15 | #:write-delivery-bundle-assembler-source 16 | 17 | #:defbundle 18 | #:bundle-assets 19 | #:bundle-system-name 20 | #:bundle-entry-point 21 | 22 | #:bundle-file 23 | #:bundle-file-source 24 | #:bundle-file-destination 25 | #:bundle-library) 26 | (:local-nicknames (:awdb :alien-works-delivery-bundle))) 27 | -------------------------------------------------------------------------------- /delivery/scripts/blobs.lisp: -------------------------------------------------------------------------------- 1 | (cl:in-package :cl-user) 2 | 3 | ;; 4 | ;; INPUT: 5 | ;; *bodge-blob-systems* 6 | ;; *foreign-library-dir* 7 | ;; 8 | ;; FROM build.lisp: 9 | ;; *delivery-bundle-directory* 10 | ;; *target-features* 11 | ;; 12 | (when *bodge-blob-systems* 13 | (asdf:load-system :bodge-blobs-support)) 14 | 15 | (when *bodge-blob-systems* 16 | (let ((dst-dir (dir *delivery-bundle-directory* *foreign-library-dir*)) 17 | (libraries (loop for system in *bodge-blob-systems* 18 | append (bodge-blobs-support:find-system-libraries-by-features 19 | system 20 | *target-features*)))) 21 | (ensure-directories-exist dst-dir) 22 | (shout "Copying foreing libraries into ~A." dst-dir) 23 | (loop for lib in libraries 24 | for path = (file (bodge-blobs-support:library-descriptor-search-path lib) 25 | (bodge-blobs-support:library-descriptor-name lib)) 26 | do (shout "Copying ~A." path) 27 | (cp dst-dir path)))) 28 | -------------------------------------------------------------------------------- /archive/scripts/bundler.lisp: -------------------------------------------------------------------------------- 1 | (cl:in-package :cl-user) 2 | 3 | ;; 4 | ;; INPUT: 5 | ;; *bundler-output-filename* 6 | ;; *bundler-name* 7 | ;; 8 | (flet ((%system-path (relative) 9 | (merge-pathnames (uiop:relativize-pathname-directory relative) 10 | *target-bundle-directory*))) 11 | (let* ((windows-p (uiop:featurep '(:or :windows :os-windows))) 12 | (app-dir (merge-pathnames *bundler-name* *delivery-bundle-directory*)) 13 | (target-bundle-file (or (provided-bundle-output-file) 14 | (format nil "~A.~A" 15 | (merge-pathnames 16 | *bundler-output-filename* 17 | (%system-path "./")) 18 | (if windows-p "zip" "tar.gz"))))) 19 | (when windows-p 20 | (mv (file app-dir "app.exe") (file app-dir "app.bin"))) 21 | (shout "Compressing ~A" app-dir) 22 | (compress target-bundle-file app-dir) 23 | (shout "Archive created at ~A." target-bundle-file))) 24 | -------------------------------------------------------------------------------- /msix/image/resources/build.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [Parameter(Mandatory)] 3 | [string]$Type, 4 | 5 | [Parameter(Mandatory)] 6 | [string]$Source, 7 | 8 | [Parameter(Mandatory)] 9 | [string]$Target 10 | ) 11 | 12 | echo "Building artifact of type '$Type' from '$Source' into '$Target'" 13 | 14 | $TmpWorkFile = New-TemporaryFile 15 | $TmpWorkDir = (New-Item -Path ($TmpWorkFile.FullName + '.dir') -Type "directory").FullName 16 | 17 | if ( "$Source" -like '*.zip' ) { 18 | Expand-Archive "$Source" "$TmpWorkDir" 19 | } elseif ( "$Source" -like '*.tar.gz' ) { 20 | Push-Location -Path "$TmpWorkDir" 21 | tar -xf "$Source" 22 | Pop-Location 23 | } else { 24 | echo "Unrecognized archive type: *.zip or *.tar.gz file expected, but got '$Source'" 25 | exit 1 26 | } 27 | 28 | $Env:ALIEN_WORKS_DELIVERY_TARGET_DIR=$Target 29 | try { 30 | & 'C:/Program Files/Steel Bank Common Lisp/sbcl.exe' --script "$TmpWorkDir/delivery-bundle/deliver.lisp" $Type 31 | } finally { 32 | Remove-Item -LiteralPath @( "$TmpWorkFile", "$TmpWorkDir" ) -Force -Recurse 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Pavel Korolev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # alien-works-delivery 2 | 3 | WIP system for delivering Common Lisp applications as executable bundles. For 4 | now it only supports `AppImage` format for Linux and `MSIX` for 5 | Windows, but `.APK` for Android and later MacOSX and iOS bundle formats are planned too. 6 | 7 | Delivery process is split into two stages: 8 | 1. This system generates source bundle with required lisp sources to build an application 9 | 1. User invokes a simple command (e.g. `ccl --eval '(load 10 | "/path/to/source-bundle.lisp" :external-format :latin-1)'`) to build this 11 | source bundle into an actual executable bundle 12 | 13 | Second step can possibly be performed anywhere appropriate architecture, 14 | required build tools and lisp implementation are available. 15 | 16 | 17 | ## Signing 18 | 19 | #### MSIX 20 | 21 | Generating new self-signed certificate and installing it as a root cert: 22 | ```powershell 23 | $cert = New-SelfSignedCertificate -DnsName ".org", ".io" -Type CodeSigning -Subject "CN=" -CertStoreLocation "cert:\LocalMachine\My" 24 | 25 | Move-Item (Join-Path "cert:\LocalMachine\My" $cert.Thumbprint) -Destination "cert:\LocalMachine\Root" 26 | ``` 27 | 28 | Signing MSIX bundle with the cert generated via above method: 29 | ```powershell 30 | SignTool sign /v /fd sha256 /sm /s Root /n C:\path-to\app.msix 31 | ``` 32 | -------------------------------------------------------------------------------- /msix/templates/appxmanifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 11 | Alien-Works Demo 12 | Alien-Works 13 | Alien-Works demo for Windows 10 x64 14 | img\app150.png 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /msix/image/Dockerfile: -------------------------------------------------------------------------------- 1 | # escape=` 2 | 3 | FROM mcr.microsoft.com/windows/servercore:ltsc2019 4 | 5 | SHELL ["cmd", "/S", "/C"] 6 | 7 | # 8 | # INIT WINDOWS BUILD TOOLS 9 | # TODO: MOVE TO MSIX IMAGE 10 | # 11 | RUN ` 12 | # Download the Build Tools bootstrapper. 13 | curl -SL --output vs_buildtools.exe https://aka.ms/vs/16/release/vs_buildtools.exe ` 14 | ` 15 | # Install Build Tools with the Microsoft.VisualStudio.Workload.AzureBuildTools workload, excluding workloads and components with known issues. 16 | && (start /w vs_buildtools.exe --quiet --wait --norestart --nocache ` 17 | --installPath "%ProgramFiles(x86)%\Microsoft Visual Studio\2019\BuildTools" ` 18 | --add Microsoft.VisualStudio.Component.Windows10SDK.19041 ` 19 | --remove Microsoft.VisualStudio.Component.Windows10SDK.10240 ` 20 | --remove Microsoft.VisualStudio.Component.Windows10SDK.10586 ` 21 | --remove Microsoft.VisualStudio.Component.Windows10SDK.14393 ` 22 | --remove Microsoft.VisualStudio.Component.Windows81SDK ` 23 | || IF "%ERRORLEVEL%"=="3010" EXIT 0) ` 24 | ` 25 | # Cleanup 26 | && del /q vs_buildtools.exe 27 | 28 | COPY resources/provision.ps1 ./ 29 | 30 | # 31 | # PROVISION ENVIRONMENT 32 | # 33 | RUN powershell -File ./provision.ps1 34 | 35 | COPY resources/init.ps1 resources/init.lisp ./ 36 | 37 | # 38 | # INITIALIZE ENVIRONMENT 39 | # 40 | RUN powershell -File ./init.ps1 41 | 42 | # 43 | # COPY REST OF RESOURCES 44 | # 45 | WORKDIR C:/ 46 | COPY resources/build.ps1 resources/sign.ps1 resources/entrypoint.ps1 ./ 47 | 48 | 49 | ENTRYPOINT ["powershell", "-File", "C:/entrypoint.ps1"] 50 | -------------------------------------------------------------------------------- /delivery/scripts/deliver.lisp: -------------------------------------------------------------------------------- 1 | (cl:in-package :cl-user) 2 | 3 | #+ecl 4 | (require 'asdf) 5 | (require 'uiop) 6 | 7 | (declaim (special *target-directory*)) 8 | 9 | (import 'alien-works-delivery-util:shout) 10 | 11 | (defparameter *target-directory* nil) 12 | (defparameter *working-directory* nil) 13 | (defparameter *bundle-directory* nil) 14 | 15 | 16 | (defun deliver () 17 | (alien-works-delivery-util:with-temporary-directory (:pathname work-dir) 18 | (let* ((bundle-type (first (uiop:command-line-arguments))) 19 | (root-dir (uiop:pathname-directory-pathname *load-pathname*)) 20 | (target-dir (or (uiop:getenv "ALIEN_WORKS_DELIVERY_TARGET_DIR") 21 | root-dir)) 22 | (bundle-dir (uiop:ensure-directory-pathname 23 | (merge-pathnames 24 | bundle-type 25 | (merge-pathnames "bundles/" root-dir))))) 26 | (when (uiop:emptyp bundle-type) 27 | (error "Bundle type must be specified.")) 28 | (shout "Delivering from ~A to ~A" bundle-dir target-dir) 29 | (setf *working-directory* (or (uiop:getenv "ALIEN_WORKS_DELIVERY_WORK_DIR") 30 | work-dir) 31 | *target-directory* target-dir 32 | *bundle-directory* bundle-dir) 33 | (ensure-directories-exist *working-directory*) 34 | (ensure-directories-exist *target-directory*) 35 | (load (merge-pathnames "build.lisp" bundle-dir))))) 36 | 37 | 38 | (defun run-delivery () 39 | (unwind-protect 40 | (handler-case 41 | (deliver) 42 | (serious-condition (c) 43 | (shout "Delivery error: ~A~&" c) 44 | (uiop:quit 1 t)))) 45 | (uiop:quit 0 t)) 46 | 47 | 48 | (run-delivery) 49 | -------------------------------------------------------------------------------- /alien-works-delivery.asd: -------------------------------------------------------------------------------- 1 | (asdf:defsystem :alien-works-delivery/util 2 | :description "Utilities also embedded into delivery bundle" 3 | :author "Pavel Korolev" 4 | :mailto "dev@borodust.org" 5 | :license "MIT" 6 | :pathname "util/" 7 | :components ((:file "uti"))) 8 | 9 | 10 | (asdf:defsystem :alien-works-delivery 11 | :description "Utility to ship alien-works based applications" 12 | :author "Pavel Korolev" 13 | :mailto "dev@borodust.org" 14 | :depends-on (:uiop :asdf :alexandria :cl-ppcre :alien-works-delivery/util 15 | :bodge-blobs-support :distignore :trivial-features) 16 | :license "MIT" 17 | :pathname "delivery/" 18 | :components ((:file "packages") 19 | (:file "registry") 20 | (:file "bundle") 21 | (:file "delivery"))) 22 | 23 | 24 | (asdf:defsystem :alien-works-delivery/appimage 25 | :description "Plugin for alien-works-delivery to deliver bundles in AppImage format" 26 | :author "Pavel Korolev" 27 | :mailto "dev@borodust.org" 28 | :depends-on (:uiop :asdf :alexandria :alien-works-delivery) 29 | :license "MIT" 30 | :pathname "appimage/" 31 | :components ((:file "appimage"))) 32 | 33 | 34 | (asdf:defsystem :alien-works-delivery/msix 35 | :description "Plugin for alien-works-delivery to deliver bundles in MSIX format" 36 | :author "Pavel Korolev" 37 | :mailto "dev@borodust.org" 38 | :depends-on (:uiop :asdf :alexandria :alien-works-delivery) 39 | :license "MIT" 40 | :pathname "msix/" 41 | :components ((:file "msix"))) 42 | 43 | 44 | (asdf:defsystem :alien-works-delivery/archive 45 | :description "Plugin for alien-works-delivery to deliver binary archives" 46 | :author "Pavel Korolev" 47 | :mailto "dev@borodust.org" 48 | :depends-on (:uiop :asdf :alexandria :alien-works-delivery) 49 | :license "MIT" 50 | :pathname "archive/" 51 | :components ((:file "archive"))) 52 | -------------------------------------------------------------------------------- /delivery/registry.lisp: -------------------------------------------------------------------------------- 1 | (cl:in-package :alien-works-delivery) 2 | 3 | 4 | (defun register-system (table system) 5 | (labels ((%register (table paths system) 6 | (let* ((key-path (first paths)) 7 | (rest-paths (rest paths)) 8 | (node (gethash key-path table))) 9 | (unless node 10 | (setf node (cons (make-hash-table :test 'equal) nil) 11 | (gethash key-path table) node)) 12 | (if rest-paths 13 | (%register (car node) rest-paths system) 14 | (push system (cdr node)))))) 15 | (when-let ((path (asdf:system-source-directory system))) 16 | (%register table (rest (pathname-directory path)) system)))) 17 | 18 | 19 | (defun make-registry-table (systems) 20 | (let ((table (make-hash-table :test 'equal))) 21 | (loop for system in systems 22 | do (register-system table system)) 23 | table)) 24 | 25 | 26 | (defun registry-root-systems (table) 27 | (loop for node being the hash-value of table 28 | for (inner . systems) = node 29 | if systems 30 | append systems 31 | else 32 | append (registry-root-systems inner))) 33 | 34 | 35 | (defun %relative-systems (node relative-path) 36 | (destructuring-bind (inner . systems) node 37 | (append 38 | (when systems 39 | (loop with path = (reverse relative-path) 40 | for system in systems 41 | collect (cons (format nil "~{~A/~}" path) system))) 42 | (loop for key-path being the hash-key of inner 43 | append (%relative-systems (gethash key-path inner) 44 | (list* key-path relative-path)))))) 45 | 46 | 47 | (defun registry-relative-systems (table) 48 | (loop for node being the hash-value of table using (hash-key key-path) 49 | for (inner . systems) = node 50 | if systems 51 | append (%relative-systems node (list key-path)) 52 | else 53 | append (registry-relative-systems inner))) 54 | -------------------------------------------------------------------------------- /archive/archive.lisp: -------------------------------------------------------------------------------- 1 | (cl:defpackage :alien-works-delivery.archive 2 | (:use :cl :alien-works-delivery-util) 3 | (:local-nicknames (:awd :alien-works-delivery) 4 | (:awdu :alien-works-delivery-util))) 5 | (cl:in-package :alien-works-delivery.archive) 6 | 7 | 8 | (defclass archive-bundle () 9 | ((output-filename :initarg :output-filename) 10 | (name :initarg :name))) 11 | 12 | 13 | (defmethod awd:make-delivery-bundle ((type (eql :archive)) bundle-def) 14 | (let ((output-name (substitute #\- #\/ 15 | (asdf:component-name 16 | (asdf:find-system 17 | (awd:bundle-system-name bundle-def)))))) 18 | (make-instance 'archive-bundle 19 | :name output-name 20 | :output-filename output-name))) 21 | 22 | 23 | (defmethod awd:prepare-delivery-bundle ((bundle archive-bundle)) 24 | (with-slots (name) bundle 25 | (let ((bundle-dir (dir awd:*delivery-bundle-directory* name))) 26 | (flet ((%appdir-path (relative) 27 | (merge-pathnames (uiop:relativize-pathname-directory relative) bundle-dir)) 28 | (%system-path (relative) 29 | (asdf:system-relative-pathname :alien-works-delivery/archive 30 | (uiop:relativize-pathname-directory relative)))) 31 | (let* ((bin-dir (%appdir-path "./"))) 32 | (ensure-directories-exist bin-dir) 33 | (ensure-directories-exist (%appdir-path "lib/"))))))) 34 | 35 | 36 | (defmethod awd:delivery-bundle-foreign-library-directory ((bundle archive-bundle)) 37 | (with-slots (name) bundle 38 | (dir name "lib/"))) 39 | 40 | 41 | (defmethod awd:delivery-bundle-asset-directory ((bundle archive-bundle)) 42 | (with-slots (name) bundle 43 | (dir name "rsc/"))) 44 | 45 | 46 | (defmethod awd:delivery-bundle-executable-path ((bundle archive-bundle)) 47 | (with-slots (name) bundle 48 | (file name "app.bin"))) 49 | 50 | 51 | (defmethod awd:delivery-bundle-assembler-parameters ((bundle archive-bundle)) 52 | (with-slots (output-filename name) bundle 53 | (list :bundler-output-filename output-filename 54 | :bundler-name name))) 55 | 56 | 57 | (defmethod awd:write-delivery-bundle-assembler-source ((bundle archive-bundle) stream) 58 | (alexandria:with-input-from-file (in (asdf:system-relative-pathname 59 | :alien-works-delivery/archive 60 | "archive/scripts/bundler.lisp")) 61 | (uiop:copy-stream-to-stream in stream))) 62 | 63 | 64 | (defmethod awd:delivery-bundle-build-features ((bundle archive-bundle)) 65 | '(:awd-archive)) 66 | -------------------------------------------------------------------------------- /msix/msix.lisp: -------------------------------------------------------------------------------- 1 | (cl:defpackage :alien-works-delivery.msix 2 | (:use :cl) 3 | (:local-nicknames (:awd :alien-works-delivery) 4 | (:awdu :alien-works-delivery-util))) 5 | (cl:in-package :alien-works-delivery.msix) 6 | 7 | 8 | (defclass msix-bundle () 9 | ((output-filename :initarg :output-filename))) 10 | 11 | 12 | (defmethod awd:make-delivery-bundle ((type (eql :msix)) bundle-def) 13 | (let ((output-name (substitute #\- #\/ 14 | (asdf:component-name 15 | (asdf:find-system 16 | (awd:bundle-system-name bundle-def)))))) 17 | (make-instance 'msix-bundle 18 | :output-filename (format nil "~A.msix" output-name)))) 19 | 20 | 21 | (defmethod awd:prepare-delivery-bundle ((bundle msix-bundle)) 22 | (let ((bundle-dir (merge-pathnames "AppDir/" awd:*delivery-bundle-directory*))) 23 | (flet ((%appdir-path (relative) 24 | (merge-pathnames (uiop:relativize-pathname-directory relative) bundle-dir)) 25 | (%system-path (relative) 26 | (asdf:system-relative-pathname :alien-works-delivery/msix 27 | (uiop:relativize-pathname-directory relative)))) 28 | (let* ((bin-dir (%appdir-path "./")) 29 | (scalable-icon-dir (%appdir-path "img/")) 30 | (metadata-dir (%appdir-path "./")) 31 | (metadata-file (merge-pathnames "appxmanifest.xml" metadata-dir))) 32 | (ensure-directories-exist bin-dir) 33 | (ensure-directories-exist (%appdir-path "lib/")) 34 | (ensure-directories-exist scalable-icon-dir) 35 | (ensure-directories-exist metadata-dir) 36 | 37 | (awdu:cp metadata-file (%system-path "msix/templates/appxmanifest.xml")) 38 | (awdu:cp scalable-icon-dir 39 | (%system-path "msix/templates/app150.png") 40 | (%system-path "msix/templates/app44.png")))))) 41 | 42 | 43 | (defmethod awd:delivery-bundle-foreign-library-directory ((bundle msix-bundle)) 44 | #P"AppDir/lib/") 45 | 46 | 47 | (defmethod awd:delivery-bundle-asset-directory ((bundle msix-bundle)) 48 | #P"AppDir/rsc/") 49 | 50 | 51 | (defmethod awd:delivery-bundle-executable-path ((bundle msix-bundle)) 52 | #P"AppDir/app.exe") 53 | 54 | 55 | (defmethod awd:delivery-bundle-assembler-parameters ((bundle msix-bundle)) 56 | (with-slots (output-filename) bundle 57 | (list :bundler-output-filename output-filename))) 58 | 59 | 60 | (defmethod awd:write-delivery-bundle-assembler-source ((bundle msix-bundle) stream) 61 | (alexandria:with-input-from-file (in (asdf:system-relative-pathname 62 | :alien-works-delivery/msix 63 | "msix/scripts/bundler.lisp")) 64 | (uiop:copy-stream-to-stream in stream))) 65 | 66 | 67 | (defmethod awd:delivery-bundle-build-features ((bundle msix-bundle)) 68 | '(:msix)) 69 | -------------------------------------------------------------------------------- /delivery/scripts/builder.lisp: -------------------------------------------------------------------------------- 1 | (cl:in-package :cl-user) 2 | 3 | ;; 4 | ;; INPUT: 5 | ;; *runner-symbol* 6 | ;; *delivery-bundle-features* 7 | ;; *base-system-name* 8 | ;; 9 | 10 | #+sbcl 11 | (declaim (sb-ext:muffle-conditions sb-ext:compiler-note)) 12 | 13 | (handler-case 14 | (flet ((runner-symbol () 15 | (destructuring-bind (symbol-name &optional package-name) 16 | (reverse (uiop:ensure-list *runner-symbol*)) 17 | (intern (format nil "~A" symbol-name) 18 | (uiop:ensure-package (or package-name :cl-user)))))) 19 | (let* ((*load-verbose* nil) 20 | (*compile-verbose* nil) 21 | (*load-print* nil) 22 | (*compile-print* nil) 23 | (work-dir (uiop:getcwd)) 24 | (asdf:*user-cache* (merge-pathnames ".cache/" work-dir)) 25 | (target-path (merge-pathnames "app.bin" work-dir))) 26 | 27 | (uiop:with-muffled-conditions ('(cl:warning)) 28 | (uiop:with-muffled-compiler-conditions () 29 | (uiop:with-muffled-loader-conditions () 30 | (pushnew :bodge-blobs-support-no-preload *features*) 31 | (asdf:load-system *base-system-name* :verbose nil) 32 | (setf asdf:*central-registry* nil 33 | asdf:*user-cache* nil) 34 | 35 | #+ecl 36 | (let ((result 37 | (first 38 | (asdf:make-build 39 | *base-system-name* 40 | :type :program 41 | :prologue-code `(progn 42 | (setf *features* 43 | (remove-duplicates 44 | (append ',*delivery-bundle-features* 45 | *features*)))) 46 | :epilogue-code `(progn 47 | (,(runner-symbol))) 48 | :move-here work-dir)))) 49 | (uiop:rename-file-overwriting-target result target-path)) 50 | 51 | #+(and lispworks android-delivery) 52 | (progn 53 | (setf *features* (append *delivery-bundle-features* *features*)) 54 | (hcl:deliver-to-android-project nil 55 | work-dir 56 | 0 57 | :no-sub-dir t)) 58 | 59 | #-(or ecl (and lispworks android-delivery)) 60 | (progn 61 | (setf *features* (remove-duplicates 62 | (append *delivery-bundle-features* *features*)) 63 | uiop:*image-entry-point* (runner-symbol)) 64 | uiop:*image-entry-point* 65 | (apply #'uiop:dump-image 66 | target-path 67 | :executable t 68 | (append 69 | (when (uiop:featurep :clozure) 70 | (list :purify t)) 71 | (when (and (uiop:featurep :sbcl) 72 | (uiop:featurep :os-windows)) 73 | (list :application-type :gui)))))))))) 74 | (serious-condition () 75 | (uiop:quit -1))) 76 | (uiop:quit 0) 77 | -------------------------------------------------------------------------------- /appimage/appimage.lisp: -------------------------------------------------------------------------------- 1 | (cl:defpackage :alien-works-delivery.appimage 2 | (:use :cl) 3 | (:local-nicknames (:awd :alien-works-delivery) 4 | (:awdu :alien-works-delivery-util))) 5 | (cl:in-package :alien-works-delivery.appimage) 6 | 7 | 8 | (defclass appimage-bundle () 9 | ((output-filename :initarg :output-filename))) 10 | 11 | 12 | (defmethod awd:make-delivery-bundle ((type (eql :appimage)) bundle-def) 13 | (let ((output-name (substitute #\- #\/ 14 | (asdf:component-name 15 | (asdf:find-system 16 | (awd:bundle-system-name bundle-def)))))) 17 | (make-instance 'appimage-bundle 18 | :output-filename (format nil "~A.AppImage" output-name)))) 19 | 20 | 21 | (defmethod awd:prepare-delivery-bundle ((bundle appimage-bundle)) 22 | (let ((bundle-dir (merge-pathnames "AppDir/" awd:*delivery-bundle-directory*))) 23 | (flet ((%appdir-path (relative) 24 | (merge-pathnames (uiop:relativize-pathname-directory relative) bundle-dir)) 25 | (%system-path (relative) 26 | (asdf:system-relative-pathname :alien-works-delivery/appimage 27 | (uiop:relativize-pathname-directory relative)))) 28 | (let* ((bin-dir (%appdir-path "usr/bin/")) 29 | (desktop-dir (%appdir-path "usr/share/applications/")) 30 | (desktop-file (merge-pathnames "app.desktop" desktop-dir)) 31 | (scalable-icon-dir (%appdir-path "usr/share/icons/hicolor/scalable/apps/")) 32 | (scalable-icon-file (merge-pathnames "app.svg" scalable-icon-dir)) 33 | (metadata-dir (%appdir-path "usr/share/metainfo/")) 34 | (metadata-file (merge-pathnames "app.appdata.xml" metadata-dir)) 35 | (apprun-file (%appdir-path "AppRun"))) 36 | (ensure-directories-exist bin-dir) 37 | (ensure-directories-exist (%appdir-path "usr/lib/")) 38 | (ensure-directories-exist desktop-dir) 39 | (ensure-directories-exist scalable-icon-dir) 40 | (ensure-directories-exist metadata-dir) 41 | 42 | (awdu:cp desktop-file (%system-path "appimage/templates/app.desktop.template")) 43 | (awdu:cp scalable-icon-file (%system-path "appimage/templates/app.svg")) 44 | (awdu:cp metadata-file (%system-path "appimage/templates/app.metainfo.xml")) 45 | (awdu:cp apprun-file (%system-path "appimage/templates/AppRun")) 46 | 47 | (awdu:ln (%appdir-path "app.desktop") 48 | (enough-namestring desktop-file bundle-dir)) 49 | (awdu:ln (%appdir-path "app.svg") 50 | (enough-namestring scalable-icon-file bundle-dir)))))) 51 | 52 | 53 | (defmethod awd:delivery-bundle-foreign-library-directory ((bundle appimage-bundle)) 54 | #P"AppDir/usr/lib/") 55 | 56 | 57 | (defmethod awd:delivery-bundle-asset-directory ((bundle appimage-bundle)) 58 | #P"AppDir/usr/share/app/") 59 | 60 | 61 | (defmethod awd:delivery-bundle-executable-path ((bundle appimage-bundle)) 62 | #P"AppDir/usr/bin/app") 63 | 64 | 65 | (defmethod awd:delivery-bundle-assembler-parameters ((bundle appimage-bundle)) 66 | (with-slots (output-filename) bundle 67 | (list :bundler-output-filename output-filename))) 68 | 69 | 70 | (defmethod awd:write-delivery-bundle-assembler-source ((bundle appimage-bundle) stream) 71 | (alexandria:with-input-from-file (in (asdf:system-relative-pathname 72 | :alien-works-delivery/appimage 73 | "appimage/scripts/bundler.lisp")) 74 | (uiop:copy-stream-to-stream in stream))) 75 | 76 | 77 | (defmethod awd:delivery-bundle-build-features ((bundle appimage-bundle)) 78 | '(:appimage)) 79 | -------------------------------------------------------------------------------- /delivery/bundle.lisp: -------------------------------------------------------------------------------- 1 | (cl:in-package :alien-works-delivery) 2 | 3 | 4 | (defvar *bundle-definition-registry* (make-hash-table :test 'equal)) 5 | 6 | (declaim (special *assets* 7 | *system-name*)) 8 | 9 | (defun proper-bundle-name (bundle-name) 10 | (string-downcase (string bundle-name)) *bundle-definition-registry*) 11 | 12 | 13 | (defclass bundle-definition () 14 | ((system-name :initarg :system-name 15 | :initform (error ":system-name missing") 16 | :reader bundle-system-name) 17 | (entry-point :initarg :entry-point 18 | :initform nil 19 | :reader bundle-entry-point) 20 | (assets :initarg :assets 21 | :initform nil 22 | :reader bundle-assets))) 23 | 24 | 25 | (defclass bundle-library () ()) 26 | 27 | (defmethod initialize-instance :after ((this bundle-library) &key definition)) 28 | 29 | 30 | (defclass bundle-file () 31 | ((source :reader bundle-file-source) 32 | (destination :reader bundle-file-destination))) 33 | 34 | 35 | (defmethod initialize-instance :after ((this bundle-file) &key definition) 36 | (with-slots (source destination) this 37 | (labels ((%system-path (relative) 38 | (asdf:system-relative-pathname *system-name* 39 | (uiop:relativize-pathname-directory relative))) 40 | (%asset-path (source) 41 | (if (listp source) 42 | (if (eq (first source) :system) 43 | (%system-path (second source)) 44 | (error "Unrecognized asset source: ~A" source)) 45 | source))) 46 | (destructuring-bind (file-source &key ((:to file-destination))) definition 47 | (setf source (%asset-path file-source) 48 | destination (if (emptyp file-destination) 49 | "./" 50 | (uiop:relativize-pathname-directory file-destination))))))) 51 | 52 | 53 | (defun parse-library-definition (library-def) 54 | (push (make-instance 'bundle-library :definition library-def) *assets*)) 55 | 56 | 57 | (defun parse-file-definition (file-def) 58 | (push (make-instance 'bundle-file :definition file-def) *assets*)) 59 | 60 | 61 | (defun parse-group-definition (group-def) 62 | (parse-asset-definition (rest group-def))) 63 | 64 | 65 | (defun parse-asset-definition (assets) 66 | (loop with libraries = (list) 67 | with files = (list) 68 | for (asset-type . asset-def) in assets 69 | do (ecase asset-type 70 | (:library (parse-library-definition asset-def)) 71 | (:file (parse-file-definition asset-def)) 72 | (:group (parse-group-definition asset-def))))) 73 | 74 | 75 | (defmethod initialize-instance :after ((this bundle-definition) &key assets) 76 | (with-slots ((bundle-assets assets) system-name) this 77 | (let ((*assets* (list)) 78 | (*system-name* system-name)) 79 | (parse-asset-definition assets) 80 | (setf bundle-assets *assets*)))) 81 | 82 | 83 | (defun register-bundle-definition (bundle-name &rest initargs &key &allow-other-keys) 84 | (setf 85 | (gethash (proper-bundle-name bundle-name) *bundle-definition-registry*) 86 | (apply #'make-instance 'bundle-definition initargs))) 87 | 88 | 89 | (defun find-bundle-definition (bundle-name) 90 | (if-let (bundle-def (gethash (proper-bundle-name bundle-name) *bundle-definition-registry*)) 91 | bundle-def 92 | (error "Bundle with name `~A` not found" bundle-name))) 93 | 94 | 95 | (defmacro defbundle (name-and-opts &body properties) 96 | (destructuring-bind (bundle-name) 97 | (ensure-list name-and-opts) 98 | (destructuring-bind (&key system entry-point assets) 99 | properties 100 | `(register-bundle-definition ',bundle-name 101 | :system-name ',(or system bundle-name) 102 | :entry-point ',entry-point 103 | :assets ',assets)))) 104 | -------------------------------------------------------------------------------- /delivery/scripts/build.lisp: -------------------------------------------------------------------------------- 1 | (cl:in-package :cl-user) 2 | 3 | (use-package :alien-works-delivery-util) 4 | 5 | ;; 6 | ;; INPUT: 7 | ;; *bundle-executable-path* 8 | ;; 9 | ;; PROVIDED VIA BUNDLE: 10 | ;; *target-directory* 11 | ;; *working-directory* 12 | ;; *bundle-directory* 13 | ;; 14 | (defun provided-target-features () 15 | (flet ((make-keyword (name) 16 | (intern name :keyword))) 17 | (mapcar #'make-keyword 18 | (mapcar #'uiop:standard-case-symbol-name 19 | (remove-if #'uiop:emptyp 20 | (uiop:split-string 21 | (uiop:getenv "ALIEN_WORKS_DELIVERY_TARGET_FEATURES") :separator " ")))))) 22 | 23 | (declaim (special *delivery-bundle-directory* 24 | *target-bundle-directory* 25 | *builder-implementation* 26 | *bundle-executable-path* 27 | *target-features*)) 28 | 29 | (with-shell-configuration (:print-command t 30 | :shell-output *standard-output* 31 | :current-directory *working-directory*) 32 | (let* ((work-dir *working-directory*) 33 | (bundle-dir *bundle-directory*) 34 | (target-directory (uiop:ensure-directory-pathname *target-directory*)) 35 | (*load-verbose* nil) 36 | (*compile-verbose* nil) 37 | (*load-print* nil) 38 | (*compile-print* nil) 39 | (asdf:*user-cache* (merge-pathnames ".cache/" work-dir)) 40 | (registry-path (merge-pathnames "../../registry/registry.lisp" 41 | *bundle-directory*)) 42 | (*delivery-bundle-directory* (dir work-dir "bundle/")) 43 | (*target-bundle-directory* target-directory) 44 | (target-executable-path (when *bundle-executable-path* 45 | (merge-pathnames *bundle-executable-path* 46 | *delivery-bundle-directory*))) 47 | (provided-impl (string-downcase (uiop:getenv "ALIEN_WORKS_DELIVERY_LISP_IMPLEMENTATION"))) 48 | (*builder-implementation* (cond 49 | ((string= provided-impl "ccl") :ccl) 50 | ((string= provided-impl "sbcl") :sbcl) 51 | ((string= provided-impl "ecl") :ecl) 52 | ((string= provided-impl "lispworks") :lispworks) 53 | (t (cond 54 | ((uiop:featurep :ccl) :ccl) 55 | ((uiop:featurep :sbcl) :sbcl) 56 | ((uiop:featurep :ecl) :ecl) 57 | ((uiop:featurep :lispworks) :lispworks) 58 | (t (error "Unsupported builder implementation: ~A" 59 | (uiop:implementation-identifier)))))))) 60 | 61 | (shout "Loading registry.") 62 | (load registry-path) 63 | 64 | (when (asdf:find-system :trivial-features nil) 65 | (asdf:load-system :trivial-features)) 66 | 67 | (let ((*target-features* (or (provided-target-features) *features*))) 68 | (shout "Target features: ~{~A~^, ~}" *target-features*) 69 | 70 | (shout "Building executable.") 71 | (apply #'shell (or 72 | (uiop:getenv "ALIEN_WORKS_DELIVERY_LISP") 73 | (first (uiop:raw-command-line-arguments))) 74 | (append 75 | (ecase *builder-implementation* 76 | (:ccl (list "--no-init")) 77 | (:sbcl (list "--no-userinit")) 78 | (:ecl (list "--norc")) 79 | (:lispworks (list "-init" "-"))) 80 | (if (eq *builder-implementation* :lispworks) 81 | (list "-eval" "(lispworks::load-all-patches)" 82 | "-load" registry-path 83 | "-build" (merge-pathnames "builder.lisp" bundle-dir)) 84 | (list "--load" registry-path 85 | "--load" (merge-pathnames "builder.lisp" bundle-dir))))) 86 | (when (uiop:file-pathname-p target-executable-path) 87 | (shout "Moving executable to ~A" target-executable-path) 88 | (mv target-executable-path (file work-dir "app.bin")) 89 | (when (uiop:featurep :unix) 90 | (shout "Ensure executable.") 91 | (shell "chmod" "+x" target-executable-path))) 92 | 93 | (shout "Preparing foreign libraries.") 94 | (load (merge-pathnames "blobs.lisp" bundle-dir)) 95 | 96 | (shout "Bundling.") 97 | (load (merge-pathnames "bundler.lisp" bundle-dir)) 98 | (shout "Done.")))) 99 | -------------------------------------------------------------------------------- /util/uti.lisp: -------------------------------------------------------------------------------- 1 | (cl:defpackage :alien-works-delivery-util 2 | (:use :cl) 3 | (:export #:string* 4 | #:string+ 5 | #:with-shell-configuration 6 | #:shell 7 | #:shout 8 | #:dir 9 | #:file 10 | #:cp 11 | #:rm 12 | #:ln 13 | #:mv 14 | #:compress 15 | #:decompress 16 | #:ensure-unix-namestring 17 | #:with-temporary-directory 18 | #:provided-bundle-output-file)) 19 | (cl:defpackage :alien-works-delivery~pristine (:use)) 20 | (cl:in-package :alien-works-delivery-util) 21 | 22 | (require 'asdf) 23 | (require 'uiop) 24 | 25 | 26 | (defvar *supress-errors* nil) 27 | (defvar *shell-output* nil) 28 | (defvar *print-shell-command* nil) 29 | 30 | 31 | (macrolet ((%ensure-package (name) 32 | `(unless (find-package ,name) 33 | (uiop:ensure-package ,name)))) 34 | (%ensure-package :sb-ext) 35 | (%ensure-package :ccl) 36 | (%ensure-package :alien-works-delivery-bundle)) 37 | 38 | 39 | (defun windowsp () 40 | (some #'uiop:featurep '(:windows :win32))) 41 | 42 | 43 | (defun string* (control &rest args) 44 | (apply #'format nil control args)) 45 | 46 | 47 | (defun string+ (&rest args) 48 | (format nil "~{~A~}" args)) 49 | 50 | 51 | (defun verbatim-shell-string (string) 52 | (let ((escape-char (if (windowsp) 53 | #\' 54 | #\\))) 55 | (with-output-to-string (out) 56 | (format out "'") 57 | (loop for ch across string 58 | if (char= ch #\') 59 | do (format out "~A'" escape-char) 60 | else 61 | do (format out "~A" ch)) 62 | (format out "'")))) 63 | 64 | 65 | (defmacro with-shell-configuration ((&key current-directory 66 | supress-errors 67 | shell-output 68 | print-command) 69 | &body body) 70 | `(let (,@(when supress-errors 71 | `((*supress-errors* ,supress-errors))) 72 | ,@(when shell-output 73 | `((*shell-output* ,shell-output))) 74 | ,@(when print-command 75 | `((*print-shell-command* ,print-command)))) 76 | (,@(if current-directory 77 | `(uiop:with-current-directory (,current-directory)) 78 | '(progn)) 79 | ,@body))) 80 | 81 | 82 | (defun shell (command &rest args) 83 | (flet ((quote-arg (arg) 84 | (cond 85 | ((and (windowsp) 86 | (stringp arg) 87 | (> (length arg) 0) 88 | (or (char= #\- (aref arg 0)) 89 | (char= #\" (aref arg 0)))) 90 | arg) 91 | ((stringp arg) (verbatim-shell-string arg)) 92 | ((keywordp arg) (string* "~(~A~)" arg)) 93 | ((pathnamep arg) (if (windowsp) 94 | (string+ "\"" (uiop:native-namestring arg) "\"") 95 | (string+ "'" (uiop:native-namestring arg) "'"))) 96 | (t (format nil "~A" arg))))) 97 | (let* ((command (format nil "~{~A ~}~{~A~^ ~}" 98 | (if (windowsp) 99 | (list "&" (verbatim-shell-string command)) 100 | (list (verbatim-shell-string command))) 101 | (mapcar #'quote-arg args))) 102 | (shell (if (windowsp) 103 | (list "powershell" "-Command") 104 | (list "sh" "-c"))) 105 | (full-command (nconc shell (list command)))) 106 | (when *print-shell-command* 107 | (shout "Executing `~A`" full-command)) 108 | (multiple-value-bind (std err code) 109 | (uiop:run-program full-command 110 | :output (or *shell-output* :string) 111 | :error-output (unless *supress-errors* 112 | *error-output*) 113 | :force-shell nil 114 | :ignore-error-status t) 115 | (declare (ignore err)) 116 | (if (= code 0) 117 | std 118 | (error "Shell command `~A` returned non-zero code (~A)" command code)))))) 119 | 120 | 121 | (defun cp (destination source &rest sources) 122 | (ensure-directories-exist 123 | (uiop:pathname-directory-pathname destination)) 124 | (if (windowsp) 125 | (shell "Copy-Item" 126 | "-LiteralPath" (format nil "~{\"~A\"~^,~}" (list* source sources)) 127 | "-Destination" destination 128 | "-Recurse" 129 | "-Force") 130 | (apply #'shell 131 | "/bin/cp" "-LR" 132 | (append 133 | (list* source sources) 134 | (list destination))))) 135 | 136 | 137 | (defun mv (destination source) 138 | (ensure-directories-exist 139 | (uiop:pathname-directory-pathname destination)) 140 | (if (windowsp) 141 | (shell "Move-Item" 142 | "-LiteralPath" source 143 | "-Destination" destination 144 | "-Force") 145 | (shell "/bin/mv" source destination))) 146 | 147 | 148 | (defun compress (destination source) 149 | (if (windowsp) 150 | (let ((tmp-path (string+ source "~.zip"))) 151 | (shell "Compress-Archive" 152 | "-LiteralPath" source 153 | "-DestinationPath" tmp-path 154 | "-Force") 155 | (mv destination tmp-path)) 156 | (multiple-value-bind (parent source) 157 | (if (uiop:file-pathname-p source) 158 | (values (uiop:pathname-directory-pathname source) (file-namestring source)) 159 | (let ((parent (uiop:pathname-parent-directory-pathname source))) 160 | (values parent (uiop:enough-pathname source parent)))) 161 | (uiop:with-current-directory (parent) 162 | (shell "/bin/tar" "-czf" destination source))))) 163 | 164 | 165 | (defun decompress (destination source) 166 | (let ((destination (uiop:ensure-directory-pathname destination))) 167 | (if (windowsp) 168 | (shell "Expand-Archive" 169 | "-Path" source 170 | "-DestinationPath" destination 171 | "-Force") 172 | (uiop:with-current-directory (destination) 173 | (shell "/bin/tar" "-xzf" source))))) 174 | 175 | 176 | (defun rm (path) 177 | (if (windowsp) 178 | (shell "Remove-Item" 179 | "-LiteralPath" path 180 | "-Recurse" 181 | "-Force") 182 | (shell "/bin/rm" "-rf" path))) 183 | 184 | 185 | (defun ln (destination source) 186 | (if (windowsp) 187 | (shell "New-Item" 188 | "-Path" destination 189 | "-ItemType" "SymbolicLink" 190 | "-Value" source 191 | "-Force") 192 | (shell "/bin/ln" "-s" source destination))) 193 | 194 | 195 | (defun shout (control &rest params) 196 | (unwind-protect 197 | (handler-case 198 | (format *standard-output* "~&~A~&" (apply #'format nil control params)) 199 | (serious-condition (c) 200 | (warn "Failed to shout `~A` with arguments ~A: ~A" control params c))) 201 | (finish-output *standard-output*))) 202 | 203 | 204 | (defun dir (base &rest pathnames) 205 | (flet ((ensure-relative-dir (dir) 206 | (uiop:ensure-directory-pathname (uiop:enough-pathname dir "/")))) 207 | (reduce #'merge-pathnames (nreverse (mapcar #'ensure-relative-dir pathnames)) 208 | :initial-value (uiop:ensure-directory-pathname base) 209 | :from-end t))) 210 | 211 | 212 | (defun file (&rest pathnames) 213 | (flet ((ensure-file (pathname) 214 | (let ((pathname (pathname pathname))) 215 | (if (uiop:directory-pathname-p pathname) 216 | (let* ((dir (pathname-directory pathname)) 217 | (namepath (pathname (first (last dir))))) 218 | (make-pathname :directory (butlast dir) 219 | :name (pathname-name namepath) 220 | :type (pathname-type namepath) 221 | :defaults pathname)) 222 | pathname)))) 223 | (multiple-value-bind (neck last) 224 | (loop for (path . rest) on pathnames 225 | if rest 226 | collect path into neck 227 | else 228 | return (values neck (ensure-file path))) 229 | (if neck 230 | (merge-pathnames (uiop:enough-pathname last "/") (apply #'dir neck)) 231 | last)))) 232 | 233 | 234 | (defun ensure-unix-namestring (path) 235 | (let ((filename (file-namestring path)) 236 | (dirs (rest (pathname-directory path)))) 237 | (format nil "~@[~A~]~{~A/~}~@[~A~]" 238 | (when (uiop:absolute-pathname-p path) 239 | (uiop:pathname-root path)) 240 | dirs 241 | filename))) 242 | 243 | 244 | (defmacro with-temporary-directory ((&key pathname) &body body) 245 | (let ((tmp-file (gensym)) 246 | (tmp-dir (gensym))) 247 | `(uiop:with-temporary-file (:pathname ,tmp-file) 248 | (let* ((,tmp-dir (merge-pathnames (format nil "~A.dir/" (pathname-name ,tmp-file)) 249 | (uiop:pathname-directory-pathname ,tmp-file))) 250 | ,@(when pathname 251 | `((,pathname ,tmp-dir)))) 252 | (unwind-protect 253 | (progn 254 | (ensure-directories-exist ,tmp-dir) 255 | ,@body) 256 | (uiop:delete-directory-tree ,tmp-dir :validate (constantly t))))))) 257 | 258 | 259 | (defun provided-bundle-output-file () 260 | (uiop:if-let (out (uiop:getenv "ALIEN_WORKS_DELIVERY_BUNDLE_TARGET_FILE")) 261 | (file out))) 262 | -------------------------------------------------------------------------------- /delivery/delivery.lisp: -------------------------------------------------------------------------------- 1 | (cl:in-package :alien-works-delivery) 2 | 3 | 4 | (defun merge-resource-pathname (relative) 5 | (asdf:system-relative-pathname :alien-works-delivery relative)) 6 | 7 | 8 | (defun print-parameters (stream &rest params &key &allow-other-keys) 9 | (let ((*package* (find-package :alien-works-delivery~pristine))) 10 | (loop for (param value) on params by #'cddr 11 | do (prin1 `(defparameter ,(alexandria:format-symbol :cl-user "*~A*" param) ',value) stream)))) 12 | 13 | 14 | (defun extract-dependency-name (dependency-designator) 15 | (if (listp dependency-designator) 16 | (ecase (first dependency-designator) 17 | (:version (extract-dependency-name (second dependency-designator))) 18 | (:feature (extract-dependency-name (third dependency-designator))) 19 | (:require (values (extract-dependency-name (second dependency-designator)) t))) 20 | dependency-designator)) 21 | 22 | 23 | (defun collect-dependencies (system) 24 | (let ((dependency-table (make-hash-table :test 'equal))) 25 | (labels ((%collect-dependencies (system) 26 | (when system 27 | (loop for dep-descriptor in (append (asdf:system-depends-on system) 28 | (asdf:system-weakly-depends-on system) 29 | (asdf:system-defsystem-depends-on system)) 30 | for (dep-name require-p) = (multiple-value-list (extract-dependency-name dep-descriptor)) 31 | for dep = (unless require-p 32 | (asdf:find-system dep-name t)) 33 | when dep 34 | do (let ((proper-dep-name (asdf:component-name dep))) 35 | (when (not (gethash proper-dep-name dependency-table)) 36 | (setf (gethash proper-dep-name dependency-table) dep) 37 | (%collect-dependencies dep))))))) 38 | (%collect-dependencies system)) 39 | (hash-table-values dependency-table))) 40 | 41 | 42 | (defun collect-root-paths (registry-table) 43 | (remove-duplicates (mapcar #'asdf:system-source-directory 44 | (registry-root-systems registry-table)) 45 | :test #'equal)) 46 | 47 | 48 | (defun collect-relative-paths (registry-table) 49 | (remove-duplicates (mapcar #'car (registry-relative-systems registry-table)) 50 | :test #'equal)) 51 | 52 | 53 | (defun copy-directory (source destination) 54 | (let* ((source (dir source)) 55 | (destination (dir destination))) 56 | (distignore:with-ignorable-directory (source) 57 | (ensure-directories-exist destination) 58 | (when (uiop:directory-exists-p source) 59 | (when-let (files (remove-if #'distignore:pathname-ignored-p (uiop:directory-files source))) 60 | (unless (apply #'cp destination files) 61 | (error "Failed to copy files into directory ~A" destination))) 62 | (loop for dir in (remove-if #'distignore:pathname-ignored-p (uiop:subdirectories source)) 63 | do (copy-directory dir (dir destination (first (last (pathname-directory dir)))))))))) 64 | 65 | 66 | (defun make-asdf-registry (base-system-name target-directory &key (if-exists :error)) 67 | (let* ((base-system (asdf:find-system base-system-name)) 68 | (systems (list* base-system (collect-dependencies base-system))) 69 | (registry-table (make-registry-table systems)) 70 | (target-directory (dir target-directory))) 71 | (when (uiop:directory-exists-p target-directory) 72 | (case if-exists 73 | (:supersede (rm target-directory)) 74 | (:error (error "Directory exist: ~A" target-directory)))) 75 | 76 | (ensure-directories-exist target-directory) 77 | (loop for path in (collect-root-paths registry-table) 78 | for target-registry-directory = (dir target-directory 79 | (first (last (pathname-directory path)))) 80 | for target-exists = (uiop:directory-exists-p target-registry-directory) 81 | when (and target-exists (eq if-exists :error)) 82 | do (error "Target directory exists: ~A" target-registry-directory) 83 | when (or (eq if-exists :overwrite) 84 | (not (uiop:directory-exists-p target-registry-directory))) 85 | do (copy-directory path target-registry-directory)) 86 | 87 | (with-output-to-file (out (file target-directory "registry.lisp") :if-exists :supersede) 88 | (print-parameters out :registry-paths (collect-relative-paths registry-table)) 89 | (append-file out (merge-resource-pathname "delivery/scripts/registry.lisp"))) 90 | systems)) 91 | 92 | 93 | (defun make-builder (bundle base-system-name runner-symbol target-path) 94 | (with-output-to-file (out target-path :if-exists :supersede) 95 | (print-parameters out 96 | :runner-symbol (if (and (symbolp runner-symbol) 97 | (not (keywordp runner-symbol))) 98 | (list (make-keyword (package-name 99 | (symbol-package runner-symbol))) 100 | (make-keyword (symbol-name runner-symbol))) 101 | runner-symbol) 102 | :base-system-name base-system-name 103 | :delivery-bundle-features (delivery-bundle-build-features bundle)) 104 | (append-file out (merge-resource-pathname "delivery/scripts/builder.lisp")))) 105 | 106 | 107 | (defun make-bundler (bundle bundler-path) 108 | (with-output-to-file (out bundler-path :if-exists :supersede) 109 | (apply #'print-parameters out (delivery-bundle-assembler-parameters bundle)) 110 | (write-delivery-bundle-assembler-source bundle out))) 111 | 112 | 113 | (defun make-bodge-blob-collector (systems collector-path blob-dir) 114 | (let ((blob-systems (loop for system in systems 115 | when (bodge-blobs-support:bodge-blob-system-p system) 116 | collect (asdf:component-name system)))) 117 | (with-output-to-file (out collector-path :if-exists :supersede) 118 | (when blob-systems 119 | (print-parameters out 120 | :bodge-blob-systems blob-systems 121 | :foreign-library-dir blob-dir) 122 | (append-file out (merge-resource-pathname "delivery/scripts/blobs.lisp")))))) 123 | 124 | 125 | (defun append-file (stream file &key element-type) 126 | (alexandria:with-input-from-file (in file :element-type element-type) 127 | (uiop:copy-stream-to-stream in stream :element-type element-type))) 128 | 129 | 130 | (defun copy-assets (assets base-dir) 131 | (flet ((%copy-asset (destination source) 132 | (let ((destination (merge-pathnames destination base-dir))) 133 | (ensure-directories-exist destination) 134 | (cp destination source)))) 135 | (loop for asset in assets 136 | when (typep asset 'bundle-file) 137 | do (%copy-asset (bundle-file-destination asset) (bundle-file-source asset))))) 138 | 139 | 140 | (declaim (special *delivery-bundle-directory* 141 | *delivery-bundle-registry* 142 | *delivery-bundle-systems*)) 143 | 144 | (defgeneric make-delivery-bundle (type bundle-def)) 145 | (defgeneric prepare-delivery-bundle (bundle)) 146 | (defgeneric delivery-bundle-foreign-library-directory (bundle)) 147 | (defgeneric delivery-bundle-asset-directory (bundle)) 148 | (defgeneric delivery-bundle-executable-path (bundle)) 149 | (defgeneric delivery-bundle-build-features (bundle)) 150 | (defgeneric delivery-bundle-assembler-parameters (bundle)) 151 | (defgeneric write-delivery-bundle-assembler-source (bundle stream)) 152 | 153 | 154 | (defun prepare-bundle-commons (bundle-def delivery-bundle-dir) 155 | (let* ((system-name (bundle-system-name bundle-def)) 156 | (*print-case* :downcase)) 157 | (ensure-directories-exist delivery-bundle-dir) 158 | 159 | (with-output-to-file (builder-stream (file delivery-bundle-dir "deliver.lisp") 160 | :if-exists :supersede) 161 | (append-file builder-stream 162 | (asdf:system-relative-pathname :alien-works-delivery/util 163 | "util/uti.lisp")) 164 | (append-file builder-stream 165 | (merge-resource-pathname "delivery/scripts/deliver.lisp"))) 166 | 167 | (make-asdf-registry system-name 168 | (merge-pathnames "registry/" 169 | delivery-bundle-dir)))) 170 | 171 | (defun prepare-bundle (bundle system-name entry-point assets 172 | systems delivery-bundle-dir) 173 | (let ((*print-case* :downcase)) 174 | (ensure-directories-exist *delivery-bundle-directory*) 175 | 176 | (prepare-delivery-bundle bundle) 177 | 178 | (make-bodge-blob-collector systems 179 | (file delivery-bundle-dir "blobs.lisp") 180 | (delivery-bundle-foreign-library-directory bundle)) 181 | 182 | (copy-assets assets 183 | (dir *delivery-bundle-directory* 184 | (delivery-bundle-asset-directory bundle))) 185 | 186 | (make-builder bundle 187 | system-name 188 | entry-point 189 | (file delivery-bundle-dir "builder.lisp")) 190 | 191 | (make-bundler bundle (file delivery-bundle-dir "bundler.lisp")) 192 | 193 | (with-output-to-file (builder-stream (file delivery-bundle-dir "build.lisp") 194 | :if-exists :supersede) 195 | (print-parameters builder-stream 196 | :bundle-executable-path (delivery-bundle-executable-path bundle)) 197 | (append-file builder-stream 198 | (merge-resource-pathname "delivery/scripts/build.lisp"))))) 199 | 200 | 201 | (defun write-bundle (bundle-path bundle-source-dir) 202 | (compress bundle-path (dir bundle-source-dir "delivery-bundle/"))) 203 | 204 | 205 | (defun assemble-delivery-bundle (bundle-name target-path &rest types) 206 | (let ((bundle-def (find-bundle-definition bundle-name))) 207 | (with-temporary-directory (:pathname tmp-delivery-bundle-dir) 208 | (let ((delivery-bundle-dir (dir tmp-delivery-bundle-dir "delivery-bundle/"))) 209 | (multiple-value-bind (systems) 210 | (prepare-bundle-commons bundle-def delivery-bundle-dir) 211 | (loop for bundle-type in types 212 | do (let* ((type-str (ppcre:regex-replace-all 213 | "[\\/]" 214 | (format nil "~(~A~)" bundle-type) 215 | "-")) 216 | (bundle-dir (dir delivery-bundle-dir "bundles/" type-str)) 217 | (*delivery-bundle-directory* bundle-dir) 218 | (bundle (make-delivery-bundle bundle-type bundle-def))) 219 | (prepare-bundle bundle 220 | (bundle-system-name bundle-def) 221 | (bundle-entry-point bundle-def) 222 | (bundle-assets bundle-def) 223 | systems 224 | bundle-dir))))) 225 | (write-bundle target-path tmp-delivery-bundle-dir)))) 226 | -------------------------------------------------------------------------------- /appimage/templates/app.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 25 | 27 | 31 | 35 | 36 | 38 | 42 | 46 | 47 | 49 | 53 | 57 | 61 | 62 | 64 | 68 | 72 | 76 | 77 | 79 | 83 | 87 | 88 | 90 | 94 | 98 | 99 | 101 | 105 | 109 | 110 | 119 | 128 | 137 | 146 | 155 | 164 | 165 | 184 | 186 | 187 | 189 | image/svg+xml 190 | 192 | 193 | 194 | 195 | 199 | 208 | MADE WITH MADE WITH MADE WITH MADE WITH MADE WITH MADE WITH 217 | 222 | 227 | 232 | 237 | 242 | Made with secret 255 | 260 | 266 | 271 | 275 | 279 | 283 | 287 | 291 | 295 | 299 | 303 | 308 | 313 | 318 | 323 | 328 | 333 | 338 | 343 | 347 | 351 | 356 | 361 | 366 | 370 | 375 | 380 | 385 | 390 | 395 | 400 | 405 | 409 | 414 | 419 | 424 | 428 | 433 | 437 | 441 | 446 | 451 | 455 | 459 | 464 | 468 | 473 | 477 | 481 | 485 | 489 | alien technology 502 | 507 | CAUTION: 520 | 525 | 529 | 533 | 537 | 542 | 547 | 552 | 557 | 562 | 563 | 564 | --------------------------------------------------------------------------------