Initial commit.

This commit is contained in:
Cor Legemaat 2025-06-30 16:15:39 +02:00
commit 878d52ee27
26 changed files with 3341 additions and 0 deletions

61
ebuild/Makefile.am Normal file
View file

@ -0,0 +1,61 @@
################################################################################
# Makefile.am
# Copyright (C) 2025 Cor Legemaat <cor@cor.za.net>
#
# This file is part of ebuild-autogen: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by the
# Free Software Foundation, version 3 of the License.
# ebuild-autogen is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# ebuild-autogen. If not, see <https://www.gnu.org/licenses/>.
################################################################################
SUBDIRS = fetchers
moddir=$(datadir)/guile/site/$(GUILE_EFFECTIVE_VERSION)/ebuild
objdir=$(libdir)/guile/$(GUILE_EFFECTIVE_VERSION)/site-ccache/ebuild
#https://www.gnu.org/software/automake/manual/html_node/Scripts.html
bin_SCRIPTS=ebuild-autogen
SOURCES = \
bash-utils.scm \
cli.scm \
defs.scm \
gen.scm \
repo.scm \
utils.scm \
version.scm
GOBJECTS = $(SOURCES:%.scm=%.go)
nobase_mod_DATA = $(SOURCES) $(NOCOMP_SOURCES)
nobase_obj_DATA = $(GOBJECTS)
# Make sure source files are installed first, so that the mtime of
# installed compiled files is greater than that of installed source
# files. See
# <http://lists.gnu.org/archive/html/guile-devel/2010-07/msg00125.html>
# for details.
guile_install_obj_files = install-nobase_obj_DATA
$(guile_install_obj_files): install-nobase_mod_DATA
EXTRA_DIST = $(SOURCES) $(NOCOMP_SOURCES) \
version.scm.in
CLEANFILES = $(GOBJECTS) ${bin_SCRIPTS}
GUILE_WARNINGS = -Wunbound-variable -Warity-mismatch -Wformat
GUILE_OPTS = -L $(abs_top_builddir)
SUFFIXES = .scm .go .in
version.scm: version.scm.in
$(file >$@,$(subst {{version}},$(VERSION),$(file <$<)))
ebuild-autogen: ebuild-autogen.in
$(file >$@,$(subst {{guile-bin}},$(GUILE),$(file <$<)))
.scm.go:
$(GUILD) compile $(GUILE_TARGET) $(GUILE_OPTS) $(GUILE_WARNINGS) -o "$@" "$<"

62
ebuild/bash-utils.scm Normal file
View file

@ -0,0 +1,62 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; bash-utils.scm
;; Copyright (C) 2025 Cor Legemaat <cor@cor.za.net>
;;
;; This file is part of ebuild-autogen: you can redistribute it and/or modify it
;; under the terms of the GNU Affero General Public License as published by the
;; Free Software Foundation, version 3 of the License.
;;
;; ebuild-autogen is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
;; more details.
;;
;; You should have received a copy of the GNU General Public License along with
;; ebuild-autogen. If not, see <https://www.gnu.org/licenses/>.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-module (ebuild bash-utils)
#:use-module (rnrs io ports)
#:use-module (ice-9 expect)
#:use-module (ice-9 popen)
#:use-module (ice-9 rdelim))
(define-public bash-source-vars
(lambda (source var-list)
""
(let* ((in-pipe (pipe))
(out-pipe (pipe O_NONBLOCK))
(pid (spawn "bash" '("bash")
#:input (car in-pipe)
#:output (cdr out-pipe)))
(answ '())
(expect-port (car out-pipe))
(expect-timeout 1))
;; Make line buffered.
(setvbuf (cdr in-pipe) 'line)
(setvbuf (cdr out-pipe) 'line)
;; Do the sourcing stuff.
(write-line source (cdr in-pipe))
;; Eliminate extra data to stdout from sourcing.
(write-line "echo \"Done sourcing!\"" (cdr in-pipe))
(expect ((lambda (s eof?)
(string=? s "Done sourcing!\n"))
(lambda () #t)))
;; Read the variables.
(set! answ
(map (lambda (var)
(write-line (string-append "echo \"${"
var
"}\"") (cdr in-pipe))
(cons var (get-line (car out-pipe))))
var-list))
;; Tel bash to exit.
(write-line "exit 0" (cdr in-pipe))
;; Close my side of the pipes
(close-port (car in-pipe))
(close-port (cdr out-pipe))
;; Close bash's side of the pipes.
(close-port (cdr in-pipe))
(close-port (car out-pipe))
;; Finish.
(waitpid pid)
answ)))

324
ebuild/cli.scm Normal file
View file

@ -0,0 +1,324 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; cli.scm
;; Copyright (C) 2025 Cor Legemaat <cor@cor.za.net>
;;
;; This file is part of ebuild-autogen: you can redistribute it and/or modify it
;; under the terms of the GNU Affero General Public License as published by the
;; Free Software Foundation, version 3 of the License.
;;
;; ebuild-autogen is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
;; more details.
;;
;; You should have received a copy of the GNU General Public License along with
;; ebuild-autogen. If not, see <https://www.gnu.org/licenses/>.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-module (ebuild cli)
#:use-module (ice-9 exceptions)
#:use-module (ice-9 format)
#:use-module (ice-9 pretty-print)
#:use-module (ice-9 receive)
#:use-module (srfi srfi-1)
#:use-module (dql dql)
#:use-module (ebuild defs)
#:use-module (ebuild repo)
#:use-module (ebuild gen)
#:use-module (ebuild version)
#:use-module (config)
#:use-module (config api)
#:use-module (config parser sexp))
(define config
;; Define our root configuration
(configuration
(name 'ebuild-autogen)
(keywords
(list
;; Switch to force writing non-eager configuration files
(switch
(name 'write) (default #f) (test boolean?) (character #f)
(synopsis "Write configuration file in local dir."))
(switch
(name 'verbosity) (default 3) (example "3")
(handler string->number) (test integer?)
(synopsis "The verbosity level."))
(switch
(name 'submodule-update) (default #false) (test boolean?)
(synopsis "Fetch update subtree source repo"))
(switch
(name 'pkg-clean) (default #false) (test boolean?)
(synopsis "Remove packages from repo that's not in autogen subtree."))
(switch
(name 'ebuild-clean) (default #false) (test boolean?)
(synopsis "Remove ebuilds no more active."))
(switch
(name 'cache-clean) (default #false) (test boolean?)
(synopsis "Remove files from cache no more needed in tree gen."))
(switch
(name 'remote-push) (default #false) (test boolean?)
(synopsis "Commit and push updated tree to remote."))
(switch
(name 'init-from) (default "") (test string?)
(example "https://www.cor.za.net/code/portage-overlay")
(synopsis "An url to initialize the repo from scratch"))
;;TODO figure out how to use non parameter options from the command
;; line instead of this.
(switch
(name 'base) (default "") (test string?)
(synopsis "Base folder different from working dir."))
;; A setting in the configuration file, if it exists.
(setting
(name 'github-token) (default "")
(handler identity) (test string?)
(synopsis "The github authentication token.")
(description "The github authentication token as a string for
authentication when pulling package updates from github."))
(setting
(name 'filecache-path)
(default (if (getenv "XDG_CACHE_HOME")
(getenv "XDG_CACHE_HOME")
(string-append (getenv "HOME")
"/.cache/ebuild-autogen")))
(example (string-append (getenv "HOME")
"/.cache/ebuild-autogen"))
(handler identity) (test string?)
(synopsis "Cache files path.")
(description "Full path to the folder where to store the file cache."))
(setting
(name 'tmp-path) (default "/tmp/ebuild-autogen/")
(example "/tmp/ebuild-autogen/")
(handler identity) (test string?)
(synopsis "Temporary files path.")
(description "Full path to the folder for the temporary files."))))
(synopsis "Auto generate Gentoo ebuild's")
(description "ebuild-autogen is a Guile scheme application to auto generate
gentoo ebuild package definitions from the \"autogen.scm\" specification for
the package from the subtree source residing in the autogen folder of the
git repository.")
(parser sexp-parser)
;; Specify where we want to install configuration files
(directory (list (if (getenv "XDG_CONFIG_HOME")
(path (given (getenv "XDG_CONFIG_HOME"))
(eager? #f))
(in-home ".config/" #:wait?))))
(version version-str)
(copyright (list 2025))
(license agpl3)
(author "Cor Legemaat")))
(define-public main
(lambda (cmd-line)
(let ((options (getopt-config-auto cmd-line config)))
(when (option-ref options 'write)
(options-write options))
;; Init repo from url if requested.
(if (not (string=? (option-ref options 'init-from) ""))
(repo-clone (option-ref options 'init-from)
(if (not (string=? (option-ref options 'base) ""))
(option-ref options 'base)
(getcwd))))
(let* ((folder (if (not (string=? (option-ref options 'base) ""))
(option-ref options 'base)
(getcwd)))
(repo (repo-root-for folder (option-ref options 'verbosity)))
(parms (list (cons 'repo repo)
(cons 'verbosity (option-ref options 'verbosity))
(cons 'cache-path (option-ref options
'filecache-path))
(cons 'tmp-path (option-ref options 'tmp-path)))))
;; Update the source repository if requested.
(if (option-ref options 'submodule-update)
(repo-update-src folder))
;; Add our repository to the load path.
(add-to-load-path (string-append repo "/autogen"))
;; Add optional parameters if exist.
(if (not (string-null? (option-ref options 'github-token)))
(set! parms
(assoc-set! parms
'github-token
(option-ref options 'github-token))))
(with-exception-handler
(lambda (exception)
(if (>= (option-ref options 'verbosity)
verbosity-warn)
(begin
(display "Failed to read the repo parameters with ")
(if (exception-with-message? exception)
(display
(simple-format
#f " exception-message: \"~a\""
(exception-message exception))))
(if (and (exception-with-message? exception)
(exception-with-irritants? exception))
(display " and"))
(if (exception-with-irritants? exception)
(display
(simple-format
#f " exception-irritants: \"~a\""
(exception-irritants exception))))
(newline)))
'())
(lambda ()
(let ((repo-mod (resolve-module `(repo-local settings))))
(if (module-variable repo-mod 'parms)
(set! parms (append parms
(module-ref repo-mod 'parms))))))
#:unwind? #t)
;; Update the repo definition files.
(cp-repo-file repo
""
"autogen"
"README-repo.org"
(option-ref options 'verbosity))
(cp-repo-file repo
""
"autogen"
"metadata"
(option-ref options 'verbosity))
(cp-repo-file repo
""
"autogen"
"profiles"
(option-ref options 'verbosity))
(cp-repo-file repo
""
"autogen"
"repositories.xml"
(option-ref options 'verbosity))
(cp-repo-file repo
""
"autogen/"
".gitignore-repo"
(option-ref options 'verbosity)
#:file-dst ".gitignore")
;; Preform ebuild generation.
(let ((pkg-list (build-pkg-list repo folder #t)))
(if (>= (option-ref options 'verbosity)
verbosity-warn)
(begin (display "package-list:") (newline)
(pretty-print pkg-list)))
(let ((cache-files-used
(append-map (lambda (pkg)
(let ((name (string->symbol (assoc-ref pkg 'name)))
(cat (string->symbol (assoc-ref pkg 'category))))
(if (>= (option-ref options 'verbosity)
verbosity-notice)
(begin (display "pkg:") (newline)
(pretty-print (append parms pkg))))
(with-exception-handler
(lambda (exception)
(if (>= (option-ref options 'verbosity)
verbosity-error)
(begin
(display "Failed to update the package ")
(display (assoc-ref pkg 'category))
(display "/")
(display (assoc-ref pkg 'name))
(display " with")
(if (exception-with-message? exception)
(display
(simple-format
#f " exception-message: \"~a\""
(exception-message exception))))
(if (and (exception-with-message? exception)
(exception-with-irritants? exception))
(display " and"))
(if (exception-with-irritants? exception)
(display
(simple-format
#f " exception-irritants: \"~a\""
(exception-irritants exception))))
(newline)))
'())
(lambda ()
(let* ((pkg-mod (resolve-module `(,cat ,name autogen)))
(pkg-parms (append parms pkg))
(distfiles-used '())
(pkgfiles-used
(if pkg-mod
(let ((releases
(if (module-variable pkg-mod 'get-releases)
((module-ref pkg-mod 'get-releases)
(append parms pkg))
'())))
(append
;; Setup the folder and links for the package.
(if (module-variable pkg-mod 'setup-package)
((module-ref pkg-mod 'setup-package)
pkg-parms)
(setup-pkg pkg-parms))
;; Generate the ebuilds.
(receive (pkg-files dist-files)
(if (module-variable pkg-mod 'generate-ebuilds)
((module-ref pkg-mod 'generate-ebuilds)
pkg-parms
releases)
(ebuild-gen pkg-parms
releases))
(set! distfiles-used
(append distfiles-used dist-files))
pkg-files)))
(setup-pkg pkg-parms))))
(if (option-ref options 'ebuild-clean)
(clean-files pkgfiles-used
(string-join (list repo
(assoc-ref pkg 'category)
(assoc-ref pkg 'name))
file-name-separator-string)
(option-ref options 'verbosity)))
(display "Done with package: ")
(display (assoc-ref pkg 'category))
(display "/")
(display (assoc-ref pkg 'name))
(newline)
distfiles-used))
#:unwind? (< 1 (length pkg-list)))))
pkg-list)))
;; Clean deprecated cache files if requested.
(if (>= (option-ref options 'verbosity)
verbosity-notice)
(begin (display "distfiles-used:") (newline)
(pretty-print cache-files-used)
(display "\"") (newline)))
(if (and (or (string=? folder repo)
(string=? folder
(string-join (list repo "autogen")
file-name-separator-string)))
(option-ref options 'cache-clean))
(clean-files cache-files-used
(option-ref options 'filecache-path)
(option-ref options 'verbosity))))
;; The extra folders in repo not in src.
(let ((repo-pkgs (build-pkg-list repo folder #f)))
(map (lambda (pkg)
(if (null? ((dql (filter (where (lambda (val)
(string=? val
(assoc-ref pkg 'category)))
'category)
(where (lambda (val)
(string=? val
(assoc-ref pkg 'name)))
'name)))
pkg-list))
(if (>= (option-ref options 'verbosity)
verbosity-warn)
(begin (display "Absolute pkg ")
(display (assoc-ref pkg 'category))
(display "/")
(display (assoc-ref pkg 'name))
(display " found.")
(newline)))))
repo-pkgs)))
;; Commit and push the updates to master if requested.
(if (option-ref options 'repo-push)
(repo-push-master))
(display "Completed successfully ...") (newline)))))

34
ebuild/defs.scm Normal file
View file

@ -0,0 +1,34 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; defs.scm
;; Copyright (C) 2025 Cor Legemaat <cor@cor.za.net>
;;
;; This file is part of ebuild-autogen: you can redistribute it and/or modify it
;; under the terms of the GNU Affero General Public License as published by the
;; Free Software Foundation, version 3 of the License.
;;
;; ebuild-autogen is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
;; more details.
;;
;; You should have received a copy of the GNU General Public License along with
;; ebuild-autogen. If not, see <https://www.gnu.org/licenses/>.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-module (ebuild defs)
#:use-module (ebuild version))
(define-public curl-useragent
(string-append "ebuild-autogen/"
(number->string version-major)
"."
(number->string version-minor)))
;; The verbosity level definitions.
(define-public verbosity-emergency 0)
(define-public verbosity-alert 1)
(define-public verbosity-critical 2)
(define-public verbosity-error 3)
(define-public verbosity-warn 4)
(define-public verbosity-notice 5)
(define-public verbosity-info 6)
(define-public verbosity-debug 7)

6
ebuild/ebuild-autogen.in Executable file
View file

@ -0,0 +1,6 @@
#!{{guile-bin}} \
-e main -s
!#
(add-to-load-path "/home/cor/local/share/guile/")
(use-modules (ebuild cli))

View file

@ -0,0 +1,48 @@
################################################################################
# Makefile.am
# Copyright (C) 2025 Cor Legemaat <cor@cor.za.net>
#
# This file is part of ebuild-autogen: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by the
# Free Software Foundation, version 3 of the License.
# ebuild-autogen is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# ebuild-autogen. If not, see <https://www.gnu.org/licenses/>.
################################################################################
moddir=$(datadir)/guile/site/$(GUILE_EFFECTIVE_VERSION)/ebuild/fetchers
objdir=$(libdir)/guile/$(GUILE_EFFECTIVE_VERSION)/site-ccache/ebuild/fetchers
SOURCES = \
ebuild.scm \
forgejo.scm \
github.scm \
pypi.scm \
raw.scm
GOBJECTS = $(SOURCES:%.scm=%.go)
nobase_mod_DATA = $(SOURCES) $(NOCOMP_SOURCES)
nobase_obj_DATA = $(GOBJECTS)
# Make sure source files are installed first, so that the mtime of
# installed compiled files is greater than that of installed source
# files. See
# <http://lists.gnu.org/archive/html/guile-devel/2010-07/msg00125.html>
# for details.
guile_install_obj_files = install-nobase_obj_DATA
$(guile_install_obj_files): install-nobase_mod_DATA
EXTRA_DIST = $(SOURCES) $(NOCOMP_SOURCES)
CLEANFILES = $(GOBJECTS)
GUILE_WARNINGS = -Wunbound-variable -Warity-mismatch -Wformat
GUILE_OPTS = -L $(abs_top_builddir)
SUFFIXES = .scm .go
.scm.go:
$(GUILD) compile $(GUILE_TARGET) $(GUILE_OPTS) $(GUILE_WARNINGS) -o "$@" "$<"

View file

@ -0,0 +1,66 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ebuild.scm
;; Copyright (C) 2025 Cor Legemaat <cor@cor.za.net>
;;
;; This file is part of ebuild-autogen: you can redistribute it and/or modify it
;; under the terms of the GNU Affero General Public License as published by the
;; Free Software Foundation, version 3 of the License.
;;
;; ebuild-autogen is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
;; more details.
;;
;; You should have received a copy of the GNU General Public License along with
;; ebuild-autogen. If not, see <https://www.gnu.org/licenses/>.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-module (ebuild fetchers ebuild)
#:use-module (ice-9 textual-ports)
#:use-module (oop goops)
#:use-module (rx irregex))
(define-public fetch-ebuilds
(lambda* (repo category package #:key
(version-filter (lambda (version) version))
(version-alter (lambda (version) version))
. extract-vars)
""
(let ((dir (opendir (string-append repo "/" category "/" package)))
(releases '()))
(do ((entry (readdir dir) (readdir dir)))
((eof-object? entry))
(let* ((ebuild-file (open-input-file (string-append repo "/"
category "/"
package "/"
entry)))
(version-match (irregex-search
`(seq (look-behind ,(string-append package "-"))
(+ (or alphanumeric "." "-"))
(look-ahead ".ebuild"))
entry))
(ebuild-data (if version-match
(get-string-all ebuild-file)
#f))
(release '()))
;; Get the keywords.
(if version-match
(begin (set! release
(list (cons "version"
(irregex-match-substring version-match))))
(map (lambda (var)
(let* ((var-match
(irregex-search
`(seq (look-behind ,(string-append var "=\""))
(+ (& (or printing num) (~ "\"")))
(look-ahead "\""))
ebuild-data)))
(if var-match
(append! release
(list (cons var
(irregex-match-substring
var-match)))))))
extract-vars)
(set! releases (append releases (list release)))))
(close ebuild-file)))
(closedir dir)
releases)))

299
ebuild/fetchers/forgejo.scm Normal file
View file

@ -0,0 +1,299 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; forgejo.scm
;; Copyright (C) 2025 Cor Legemaat <cor@cor.za.net>
;;
;; This file is part of ebuild-autogen: you can redistribute it and/or modify it
;; under the terms of the GNU Affero General Public License as published by the
;; Free Software Foundation, version 3 of the License.
;;
;; ebuild-autogen is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
;; more details.
;;
;; You should have received a copy of the GNU General Public License along with
;; ebuild-autogen. If not, see <https://www.gnu.org/licenses/>.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-module (ebuild fetchers forgejo)
#:use-module (ebuild defs)
#:use-module (ebuild fetchers raw)
#:use-module (ebuild utils)
#:use-module (curl)
#:use-module (json)
#:use-module (ice-9 pretty-print)
#:use-module (oop goops)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-43)
#:use-module (rx irregex)
#:use-module (dql dql))
(define api-base-url "/api/v1/repos/")
(define check-forgejo-errors
(lambda (responce)
""
;; TODO
'()))
(define fetch-forgejo-pages
(lambda* (url auth-token verbosity #:optional (page 1))
""
(let ((curl-handle (curl-easy-init)))
(if (>= verbosity verbosity-info)
(begin (display "src-url: ") (display url) (newline)))
(curl-easy-setopt curl-handle
'url
(string-append url
"?limit=100&page="
(number->string page)))
(curl-easy-setopt curl-handle 'useragent curl-useragent)
(if (string<> auth-token "")
(curl-easy-setopt curl-handle
'httpheader
(list (string-append "Authorization: token "
auth-token))))
(let* ((responce (curl-easy-perform curl-handle)))
(if (>= verbosity verbosity-debug)
(begin (display "json = ") (display responce) (newline)))
(if responce
(begin (check-forgejo-errors responce)
(let ((scm-responce (json-string->scm responce)))
(if (< 100 (vector-length scm-responce))
(vector-append (fetch-forgejo-pages url
auth-token
verbosity
(+1 page)))
(begin (if (>= verbosity verbosity-info)
(pretty-print scm-responce))
scm-responce))))
(error (string-append "Forgejo fetch failed with error "
(curl-error-string)
"\n")))))))
(define get-releases
(lambda (host user repo auth-token version-filter verbosity)
""
((dql (select (filter (where version-filter
"tag_name"))
(parm-as "version" "tag_name")
(parm-as "date" "created_at")))
(fetch-forgejo-pages (string-append host
api-base-url
user
"/"
repo
"/releases")
auth-token
verbosity))))
(define get-tags
(lambda (host user repo auth-token version-filter verbosity)
""
((dql (select (filter (where version-filter
"name"))
(parm-as "version" "name")
(parm "commit" "sha")))
(fetch-forgejo-pages (string-append host
api-base-url
user
"/"
repo
"/tags")
auth-token
verbosity))))
(define get-rel-assets
(lambda (host user repo auth-token name-prefix version-alter releases)
"TODO Won't work!!!"
;; (display "for-releases:") (newline)
;; (pretty-print releases)
(map (lambda (release)
;; (display "for-release:") (newline)
;; (pretty-print release)
(let* ((version (assoc-ref release "version"))
(version-final (version-alter version))
(base-asset-url (string-append host "/" user "/" repo))
(curl-handle (curl-easy-init))
(json "")
(scm ""))
(curl-easy-setopt curl-handle
'url
(string-append host api-base-url
user
"/"
repo
"/releases/"
version))
(curl-easy-setopt curl-handle 'useragent curl-useragent)
(if (string<> auth-token "")
(curl-easy-setopt curl-handle
'httpheader
(list (string-append "Authorization: token "
auth-token))))
;;(curl-easy-setopt curl-handle 'max-time 120)
;;(curl-easy-setopt curl-handle 'connect-timeout 30)
(set! json (curl-easy-perform curl-handle))
;; (display "json=") (display json) (newline)
(if (not json)
(error (string-append "GitHub release asset fetch "
"failed with error "
(curl-error-string)
"\n")))
(check-forgejo-errors json)
(set! scm (json-string->scm json))
(assoc-set! release "version" version-final)
(append release
(list (cons "repo-user" user))
(list (cons "repo-repo" repo))
;;TODO append this with list of custom assets.
(list
(list "assets"
(list (cons "uri" (string-append base-asset-url
"/archive/"
version
".tar.gz"))
(cons "name" (string-append name-prefix
"-"
version-final
".tar.gz"))
(cons "type" "tar.gz"))
(list (cons "uri" (string-append base-asset-url
"/archive/"
version
".tar.gz"))
(cons "name" (string-append name-prefix
"-"
version-final
".zip"))
(cons "type" "zip")))))))
releases)))
(define get-tag-assets
(lambda (host user repo name-prefix version-alter tags)
;; (display "tags = ")
;; (pretty-print tags)
(map (lambda (tag)
(let* ((version-final (version-alter (assoc-ref tag "version")))
(version-name (assoc-ref tag "version"))
(base-asset-url (string-append host "/" user "/" repo)))
(assoc-set! tag "version" version-final)
(append tag
(list (cons "repo-user" user))
(list (cons "repo-name" repo))
(list
(list "assets"
(list (cons "uri" (string-append base-asset-url
"/archive/"
version-name
".tar.gz"))
(cons "name" (string-append name-prefix
"-"
version-final
".tar.gz"))
(cons "type" "tar.gz"))
(list (cons "uri" (string-append base-asset-url
"/archive/"
version-name
".tar.gz"))
(cons "name" (string-append name-prefix
"-"
version-final
".zip"))
(cons "type" "zip")))))))
tags)))
(define-public fetch-forgejo
(lambda* (host user repo querry verbosity #:key
(auth-token "")
(version-filter (lambda (version)
(irregex-search
'(seq bos
(+ num)
(* (seq "." (+ num))))
version)))
(version-alter (lambda (version)
(let ((my-match (irregex-search
'(seq (+ num)
(* (seq "." (+ num))))
version)))
(if my-match
(irregex-match-substring my-match)
#f))))
(file-prefix repo))
""
(display "Fetching forgejo repo ")
(display repo)
(display " from ")
(display user)
(display " at ")
(display host)
(newline)
(letrec* ((releases
(case querry
((releases) (get-releases host
user
repo
auth-token
version-filter
verbosity))
((tags) (get-tags host
user
repo
auth-token
version-filter
verbosity))
(else (error (string-append "Error github can only "
"querry tags or releases.")))))
(assets (case querry
((releases) (get-rel-assets host user repo auth-token
file-prefix version-alter
releases))
((tags) (get-tag-assets host user repo
file-prefix version-alter
releases))
(else '()))))
assets)))
(define-public extract-forgejo-release
(lambda (release tmp-path)
(let* ((sha7 (assoc-ref release "sha7"))
(extracted-path (string-append tmp-path
"/"
(assoc-ref release "github-user")
"-"
(assoc-ref release "github-repo")
"-" sha7))
(file-path (fetch-raw-release release "tar.gz")))
(if (access? extracted-path F_OK)
(system* "/bin/rm" "-r" extracted-path))
(mkpath tmp-path)
(system* "/bin/tar"
"-xf" file-path "--directory"
tmp-path)
extracted-path)))
(define-public fetch-forgejo-prefixed
(lambda* (host user repo querry verbosity prefix #:key
(auth-token "")
(file-prefix repo))
""
(fetch-forgejo host user repo querry verbosity
#:auth-token auth-token
#:version-filter
(lambda (version)
(irregex-search
`(seq ,prefix
(+ num)
(* (seq "." (+ num))))
version))
#:version-alter
(lambda (version)
(let ((my-match (irregex-search
`(seq (look-behind ,prefix)
(+ num)
(* (seq "." (+ num))))
version)))
(if my-match
(irregex-match-substring my-match)
#f)))
#:file-prefix file-prefix)))

317
ebuild/fetchers/github.scm Normal file
View file

@ -0,0 +1,317 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; github.scm
;; Copyright (C) 2025 Cor Legemaat <cor@cor.za.net>
;;
;; This file is part of ebuild-autogen: you can redistribute it and/or modify it
;; under the terms of the GNU Affero General Public License as published by the
;; Free Software Foundation, version 3 of the License.
;;
;; ebuild-autogen is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
;; more details.
;;
;; You should have received a copy of the GNU General Public License along with
;; ebuild-autogen. If not, see <https://www.gnu.org/licenses/>.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-module (ebuild fetchers github)
#:use-module (ebuild defs)
#:use-module (ebuild fetchers raw)
#:use-module (ebuild utils)
#:use-module (curl)
#:use-module (json)
#:use-module (ice-9 pretty-print)
#:use-module (oop goops)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-43)
#:use-module (rx irregex)
#:use-module (dql dql))
(define api-base-url "https://api.github.com/repos/")
(define check-github-errors
(lambda (responce)
""
(cond ((string-contains responce "API rate limit exceeded for")
(error (string-append "error: GitHub API rate limit exceeded, "
"please setup a token.")))
((string-contains responce
(string-append "Request forbidden by administrative"
" rules. Please make sure your "
"request has a User-Agent header"))
(error (string-append "error: GitHub API requires a user agent, "
"should have been supplied!"))))))
(define fetch-github-pages
(lambda* (url token verbosity #:optional (page 1))
""
(let ((curl-handle (curl-easy-init)))
(curl-easy-setopt curl-handle
'url
(string-append url
"?per_page=100&page="
(number->string page)))
(curl-easy-setopt curl-handle 'useragent curl-useragent)
(if (string<> token "")
(curl-easy-setopt curl-handle
'httpheader
(list (string-append "Authorization: token "
token))))
(let* ((responce (curl-easy-perform curl-handle)))
(if (>= verbosity verbosity-debug)
(begin (display "json = ") (display responce) (newline)))
(if responce
(begin (check-github-errors responce)
(let ((scm-responce (json-string->scm responce)))
(if (< 100 (vector-length scm-responce))
(vector-append (fetch-github-pages url
verbosity
(+1 page)))
(begin (if (>= verbosity verbosity-info)
(pretty-print scm-responce))
scm-responce))))
(error (string-append "GitHub fetch failed with error "
(curl-error-string)
"\n")))))))
(define get-releases
(lambda (user repo token version-filter verbosity)
""
((dql (select (filter (where version-filter
"tag_name"))
(parm-as "version" "tag_name")
(parm-as "date" "created_at")))
(fetch-github-pages (string-append api-base-url
user
"/"
repo
"/releases")
token
verbosity))))
(define get-tags
(lambda (user repo token version-filter verbosity)
""
((dql (select (filter (where version-filter
"name"))
(parm-as "version" "name")
(parm "commit" "sha")))
(fetch-github-pages (string-append api-base-url
user
"/"
repo
"/tags")
token
verbosity))))
(define get-rel-assets
(lambda (user repo token name-prefix version-alter releases)
""
;; (display "for-releases:") (newline)
;; (pretty-print releases)
(map (lambda (release)
;; (display "for-release:") (newline)
;; (pretty-print release)
(let* ((version (assoc-ref release "version"))
(version-final (version-alter version))
(curl-handle (curl-easy-init))
(base-asset-url (string-append "https://github.com/"
user
"/"
repo))
(sha "")
(sha7 "")
(json "")
(scm ""))
(curl-easy-setopt curl-handle
'url
(string-append api-base-url
user
"/"
repo
"/git/refs/tags/"
version))
(curl-easy-setopt curl-handle 'useragent curl-useragent)
(if (string<> token "")
(curl-easy-setopt curl-handle
'httpheader
(list (string-append "Authorization: token "
token))))
;;(curl-easy-setopt curl-handle 'max-time 120)
;;(curl-easy-setopt curl-handle 'connect-timeout 30)
(set! json (curl-easy-perform curl-handle))
;; (display "json=") (display json) (newline)
(if (not json)
(error (string-append "GitHub release asset fetch "
"failed with error "
(curl-error-string)
"\n")))
(check-github-errors json)
(set! scm (json-string->scm json))
;;(display "scm:") (newline) (pretty-print scm)
(set! sha (assoc-ref (car ((dql (select (parm "object" "sha")))
(list scm)))
"sha"))
;;(display "sha=") (display sha) (newline)
(set! sha7 (string-take sha 7))
(assoc-set! release "version" version-final)
(append release
(list (cons "sha" sha))
(list (cons "github-user" user))
(list (cons "github-repo" repo))
(list (cons "sha7" sha7))
;;TODO append this with list of custom assets.
(list
(list "assets"
(list (cons "uri" (string-append base-asset-url
"/tarball/"
sha7))
(cons "name" (string-append name-prefix
"-"
version-final
"-"
sha7
".tar.gz"))
(cons "type" "tar.gz"))
(list (cons "uri" (string-append base-asset-url
"/zipball/"
sha7))
(cons "name" (string-append name-prefix
"-"
version-final
"-"
sha7
".zip"))
(cons "type" "zip")))))))
releases)))
(define get-tag-assets
(lambda (user repo name-prefix version-alter tags)
;; (display "tags = ")
;; (pretty-print tags)
(map (lambda (tag)
(let* ((sha (assoc-ref tag "sha"))
(sha7 (string-take sha 7))
(version (assoc-ref tag "version"))
(version-final (version-alter version))
(base-asset-url (string-append "https://github.com/"
user
"/"
repo)))
(assoc-set! tag "version" version-final)
(append tag
(list (cons "github-user" user))
(list (cons "github-repo" repo))
(list (cons "sha7" sha7))
(list
(list "assets"
(list (cons "uri" (string-append base-asset-url
"/tarball/"
sha7))
(cons "name" (string-append name-prefix
"-"
version-final
"-"
sha7
".tar.gz"))
(cons "type" "tar.gz"))
(list (cons "uri" (string-append base-asset-url
"/zipball/"
sha7))
(cons "name" (string-append name-prefix
"-"
version-final
"-"
sha7
".zip"))
(cons "type" "zip")))))))
tags)))
(define-public fetch-github
(lambda* (user repo token querry verbosity #:key
(version-filter (lambda (version)
(irregex-search
'(seq bos
(+ num)
(* (seq "." (+ num)))
eos)
version)))
(version-alter (lambda (version)
(let ((my-match (irregex-search
'(seq (+ num)
(* (seq "." (+ num))))
version)))
(if my-match
(irregex-match-substring my-match)
#f))))
(file-prefix repo))
""
(display "Fetching ")
(display repo)
(display " from ")
(display user)
(display " at github.")
(newline)
(letrec* ((releases
(case querry
((releases) (get-releases user
repo
token
version-filter
verbosity))
((tags) (get-tags user repo token version-filter verbosity))
(else (error (string-append "Error github can only "
"querry tags or releases.")))))
(assets (case querry
((releases) (get-rel-assets user repo token
file-prefix version-alter
releases))
((tags) (get-tag-assets user repo
file-prefix version-alter
releases))
(else '()))))
assets)))
(define-public extract-github-release
(lambda (release)
(let* ((sha7 (assoc-ref release "sha7"))
(extracted-path (string-append (assoc-ref release 'tmp-path)
"/"
(assoc-ref release "github-user")
"-"
(assoc-ref release "github-repo")
"-" sha7))
(file-path (fetch-raw-release release "tar.gz")))
(if (access? extracted-path F_OK)
(system* "/bin/rm" "-r" extracted-path))
(mkpath (assoc-ref release 'tmp-path))
(system* "/bin/tar"
"-xf" file-path "--directory"
(assoc-ref release 'tmp-path))
(values extracted-path (basename file-path)))))
(define-public fetch-github-prefixed
(lambda* (user repo token querry verbosity prefix #:key
(file-prefix repo)
(display-data #f))
""
(fetch-github user repo token querry verbosity
#:version-filter
(lambda (version)
(irregex-search
`(seq ,prefix
(+ num)
(* (seq "." (+ num)))
eos)
version))
#:version-alter
(lambda (version)
(let ((my-match (irregex-search
`(seq (look-behind ,prefix)
(+ num)
(* (seq "." (+ num))))
version)))
(if my-match
(irregex-match-substring my-match)
#f)))
#:file-prefix file-prefix)))

91
ebuild/fetchers/pypi.scm Normal file
View file

@ -0,0 +1,91 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; pypi.scm
;; Copyright (C) 2025 Cor Legemaat <cor@cor.za.net>
;;
;; This file is part of ebuild-autogen: you can redistribute it and/or modify it
;; under the terms of the GNU Affero General Public License as published by the
;; Free Software Foundation, version 3 of the License.
;;
;; ebuild-autogen is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
;; more details.
;;
;; You should have received a copy of the GNU General Public License along with
;; ebuild-autogen. If not, see <https://www.gnu.org/licenses/>.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-module (ebuild fetchers pypi)
#:use-module (ebuild defs)
#:use-module (ebuild fetchers raw)
#:use-module (ebuild utils)
#:use-module (curl)
#:use-module (json)
#:use-module (ice-9 pretty-print)
#:use-module (oop goops)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-43)
#:use-module (rx irregex)
#:use-module (dql dql))
(define fetch-pypi-pkg
(lambda (pkg display-data)
""
(let ((curl-handle (curl-easy-init)))
(curl-easy-setopt curl-handle
'url
(string-append "https://pypi.org/simple/"
pkg
"/"))
(curl-easy-setopt curl-handle 'useragent curl-useragent)
(curl-easy-setopt curl-handle
'httpheader
(list (string-append "Accept: "
"application/vnd.pypi.simple.v1+json")))
(let* ((responce (curl-easy-perform curl-handle)))
;;(display "json = ") (display responce) (newline)
(if responce
(let ((scm-responce (json-string->scm responce)))
(begin (if display-data
(pretty-print scm-responce))
scm-responce))
(error (string-append "PyPI fetch failed with error "
(curl-error-string)
"\n")))))))
(define-public fetch-pypi
(lambda* (pkg #:key (file-types (list "tar.gz")) (display-data #f))
""
(let* ((data (fetch-pypi-pkg pkg display-data))
(versions (vector->list (assoc-ref data "versions")))
(files (assoc-ref data "files")))
(map (lambda (version)
(list (cons "version" version)
(car (car ((dql (select (filter (where (lambda (file-name)
(if (string? file-name)
(string-contains file-name
(string-append "-"
version
"."
(car file-types)))
#f))
"filename"))
(parm-as "date" "upload-time")))
files)))
(cons "assets"
(map (lambda (type)
(append (car ((dql (select (filter (where (lambda (file-name)
(if (string? file-name)
(string-contains file-name
(string-append "-"
version
"."
type))
#f))
"filename"))
(parm-as "uri" "url")
(parm-as "name" "filename")
(parm "hashes" "sha256")))
files))
(list (cons "type" type))))
file-types))))
versions))))

122
ebuild/fetchers/raw.scm Normal file
View file

@ -0,0 +1,122 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; raw.scm
;; Copyright (C) 2025 Cor Legemaat <cor@cor.za.net>
;;
;; This file is part of ebuild-autogen: you can redistribute it and/or modify it
;; under the terms of the GNU Affero General Public License as published by the
;; Free Software Foundation, version 3 of the License.
;;
;; ebuild-autogen is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
;; more details.
;;
;; You should have received a copy of the GNU General Public License along with
;; ebuild-autogen. If not, see <https://www.gnu.org/licenses/>.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-module (ebuild fetchers raw)
#:use-module (ebuild defs)
#:use-module (ebuild utils)
#:use-module (curl)
#:use-module (rx irregex)
#:use-module (ice-9 pretty-print)
#:use-module (ice-9 regex)
#:use-module (ice-9 string-fun)
#:use-module (srfi srfi-1)
#:use-module (dql dql))
(define-public fetch-raw
(lambda* (url extract-file extract-version asset-types #:key
(uri-prefix "")
(version-filter (lambda (version) #t))
(version-alter (lambda (version) version)))
"extract-file should extract the file name till the dot before the type."
(let ((curl-handle (curl-easy-init)))
(curl-easy-setopt curl-handle 'url url)
(curl-easy-setopt curl-handle 'useragent curl-useragent)
(let ((responce (curl-easy-perform curl-handle)))
(let ((releases (extract-file responce)))
(filter-map
(lambda (release)
(let ((version (version-alter (extract-version release))))
(if (version-filter version)
(list (cons "version" version)
(cons "assets"
(map
(lambda (type)
(list
(cons "uri"
(string-append
(if (string=? uri-prefix "")
(if (string-match
"^(http://|https://|ftp://)"
release)
"" url)
uri-prefix)
release type))
(cons "name" (string-append release type))
(cons "type" type)))
asset-types)))
#f)))
releases))))))
(define-public fetch-raw-html
(lambda* (url file-prefix
asset-types #:key
(uri-prefix "")
(version-filter (lambda (version) #t))
(version-alter (lambda (version) version))
(extract-version
(lambda (file)
(let ((my-match (irregex-search `(seq (+ num) "."
(+ num) "."
(+ num))
file)))
(if my-match
(irregex-match-substring my-match)))))
(file-postfix (car asset-types)))
""
(fetch-raw url
(lambda (responce)
;; Break up in lines, otherwise rx extremely expensive.
(filter-map
(lambda (line)
(let ((rx-match
(irregex-search
`(seq (look-behind "<a href=\"")
,file-prefix
(seq (+ num) "." (+ num) "." (+ num))
"."
(look-ahead ,(string-append file-postfix
"\">")))
line)))
(if rx-match
(irregex-match-substring rx-match)
#f)))
(string-split responce #\newline)))
extract-version
asset-types
#:uri-prefix uri-prefix
#:version-filter version-filter
#:version-alter version-alter)))
(define-public fetch-raw-release
(lambda (release type)
""
(let* ((my-file-data
(car ((dql (select (filter (select (parm-val "assets"))
(where (lambda (x)
(string=? x type))
"type"))
(parm "uri")
(parm "name")))
release)))
(uri (assoc-ref my-file-data "uri"))
(name (assoc-ref my-file-data "name"))
(file-path (string-append (assoc-ref release 'cache-path)
"/"
name)))
(mkpath (assoc-ref release 'cache-path))
(if (not (access? file-path F_OK))
(system* "/usr/bin/wget" "-O" file-path uri))
file-path)))

408
ebuild/gen.scm Normal file
View file

@ -0,0 +1,408 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; gen.scm
;; Copyright (C) 2025 Cor Legemaat <cor@cor.za.net>
;;
;; This file is part of ebuild-autogen: you can redistribute it and/or modify it
;; under the terms of the GNU Affero General Public License as published by the
;; Free Software Foundation, version 3 of the License.
;;
;; ebuild-autogen is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
;; more details.
;;
;; You should have received a copy of the GNU General Public License along with
;; ebuild-autogen. If not, see <https://www.gnu.org/licenses/>.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-module (ebuild gen)
#:use-module (ebuild utils)
#:use-module (ebuild defs)
#:use-module (dql dql)
#:use-module (rx irregex)
#:use-module (ice-9 pretty-print)
#:use-module (ice-9 string-fun)
#:use-module (ice-9 textual-ports)
#:use-module (srfi srfi-1))
;;Function to generate template from ebuild with variable substitution.
(define-public ebuild-from-tmpl
(lambda* (vars verbosity #:key
(tmpl (string-append (assoc-ref vars 'name) ".tmpl"))
ignore-diff)
""
(letrec*
((input-port (open-input-file (string-join (list (assoc-ref vars 'repo)
"autogen"
(assoc-ref vars 'category)
(assoc-ref vars 'name)
tmpl)
file-name-separator-string)))
(data (append (list "# Auto generated from autogen.scm")
;; Remove mode-line of tmpl file.
(let ((raw (string-split (get-string-all input-port) #\lf)))
(if (irregex-search '(seq (+ "#")
(+ space)
"-*-"
(+ space))
(car raw))
(cdr raw)
raw))))
(traverse-list
(lambda (pre no data)
(traverse (string-append (if (symbol? pre)
(symbol->string pre)
pre)
"["
(number->string no)
"]")
(car data))
(if (not (null? (cdr data)))
(traverse-list pre (1+ no) (cdr data)))))
(traverse-alist
(lambda (pre data)
(for-each (lambda (var)
(if (and (string? (car var))
(string? (cdr var)))
(replace (if (eq? (string-length pre) 0)
(car var)
(string-append pre
"."
(car var)))
(cdr var))
(traverse (if (eq? (string-length pre) 0)
(car var)
(string-append pre
"."
(car var)))
(cdr var))))
data)))
(traverse (lambda (pre data)
(cond ((alist? data)
(traverse-alist pre data))
((list? data)
(traverse-list pre 0 data))
((number? data)
(replace pre (number->string data)))
((string? data) (replace pre data))
(else (error (string-append "Error! Don't know how "
"to process \""
(object->string data)
"\" data type."))))))
(replace
(lambda (var val)
(let ((var-str (string-append "{{"
(if (symbol? var)
(symbol->string var)
var)
"}}")))
(if (>= verbosity verbosity-info)
(begin (display "Replacing \"") (display var-str)
(display "\" with \"") (display val)
(display "\"") (newline)))
(set! data (map (lambda (line)
(string-replace-substring line
var-str
val))
data))))))
(traverse "" vars)
(let* ((folder-out (string-join (list (assoc-ref vars 'repo)
(assoc-ref vars 'category)
(assoc-ref vars 'name))
file-name-separator-string))
(rel (last-ebuild-rel folder-out
(assoc-ref vars 'name)
(assoc-ref vars "version")))
(ebuild-name (lambda (rel)
(string-append (assoc-ref vars 'name)
"-"
(assoc-ref vars "version")
(if (< 0 rel)
(string-append "-r"
(number->string rel))
"")
".ebuild"))))
(if (file-exists? (string-join (list folder-out
(ebuild-name rel))
file-name-separator-string))
(if (and (not ignore-diff)
(diff? data
(let* ((port-ebuild (open-input-file
(string-join (list folder-out
(ebuild-name rel))
file-name-separator-string)))
(data-ebuild (get-string-all port-ebuild)))
(close port-ebuild)
(string-split data-ebuild #\newline))
#:allow (lambda (a b)
(let* ((rx '(seq bos "#"))
(match-a (irregex-search rx a))
(match-b (irregex-search rx b)))
(if (and match-a match-b)
#false #true)))))
(let ((output-port (open-output-file
(string-join (list folder-out
(ebuild-name (1+ rel)))
file-name-separator-string))))
(display (string-join data "\n") output-port)
(close output-port)
;;(display "data-diff!!!") (newline)
(ebuild-name (1+ rel)))
(ebuild-name rel))
(let ((output-port (open-output-file
(string-join (list folder-out
(ebuild-name rel))
file-name-separator-string))))
(display (string-join data "\n") output-port)
(close output-port)
(ebuild-name rel)))))))
;; filter keep components
(define-public ebuild-fkc
(lambda (vlist-in comps-in)
""
(letrec*
((base (lambda (vlist comps)
(if (null? comps)
(cdr (car vlist))
(let ((clist (sort (delete-duplicates
(map (lambda (vers)
(if (null? (car vers))
-1
(car (car vers))))
vlist))
<)))
(map (lambda (vcomp)
(append-map
(lambda (sts)
(cond ((string? sts)
(list (list vcomp) sts))
((null? (car sts))
(append (list vcomp) (cdr sts)))
((not (list? (car (car sts))))
(list (list (append (list vcomp)
(car sts))
(second sts))))
(else
(map (lambda (tst)
(list (append (list vcomp)
(car tst))
(second tst)))
sts))))
(base (map (lambda (ver)
(list (if (null? (car ver))
'()
(cdr (car ver)))
(car (cdr ver))))
(filter (lambda (vers)
(= vcomp
(if (null? (car vers))
-1
(car (car vers)))))
vlist))
(cdr comps))))
(take-right clist
(min (length clist)
;; 0=all.
(if (zero? (car comps))
(length clist)
(car comps))))))))))
;;Works, but probably only for 3 components so shit solution.
(append-map (lambda (vmaj)
vmaj)
(base vlist-in comps-in)))))
(define default-version-components
(lambda (version)
""
(map (lambda (component)
(string->number component))
(irregex-split #\.
(irregex-match-substring
(irregex-search '(seq (+ num) (+ (seq "." (+ num))))
version))))))
(define-public ebuild-version-filter
(lambda* (releases #:key
(keep-components (if (assoc-ref releases 'keep-vers-comps)
(assoc-ref releases 'keep-vers-comps)
(list 1 1 1)))
(version-components default-version-components)
(keep (lambda (version) #f))
(drop (lambda (version) #f)))
""
(let* ((vlist (map (lambda (release)
(list (version-components (assoc-ref release
"version"))
(assoc-ref release "version")))
releases))
(vlist-filtered
(append (filter (lambda (vers)
(if (keep (second vers)) vers #f))
vlist)
(filter (lambda (vers)
(if (drop (second vers)) #f vers))
(ebuild-fkc vlist keep-components)))))
(filter-map (lambda (rel)
(if (any (lambda (vers)
(string= (assoc-ref rel "version")
(second vers)))
vlist-filtered)
(append (list (cons "version-components"
(version-components
(assoc-ref rel
"version"))))
rel)
#f))
releases))))
(define-public ebuild-cp-man
(lambda (parms)
""
(let* ((folder-in (string-join (list (assoc-ref parms 'repo)
"autogen"
(assoc-ref parms 'category)
(assoc-ref parms 'name))
file-name-separator-string))
(folder-out (string-join (list (assoc-ref parms 'repo)
(assoc-ref parms 'category)
(assoc-ref parms 'name))
file-name-separator-string))
(rel-in (last-ebuild-rel folder-in
(assoc-ref parms 'name)
(assoc-ref parms "version")))
(rel-out (last-ebuild-rel folder-out
(assoc-ref parms 'name)
(assoc-ref parms "version")))
(ebuild-name (lambda (rel)
(string-append (assoc-ref parms 'name)
"-"
(assoc-ref parms "version")
(if (< 0 rel)
(string-append "-r"
(number->string rel))
"")
".ebuild"))))
(if rel-in
(let ((path-in (string-join (list folder-in
(ebuild-name rel-in))
file-name-separator-string))
(path-out (string-join (list folder-out
(ebuild-name rel-out))
file-name-separator-string)))
(if (and (< 0 rel-in)
(< verbosity-warn (assoc-ref parms 'verbosity)))
(begin (display "Warning: Source ebuild should not have ")
(display "revisions, handled automatically!")
(newline)))
(if (file-exists? path-in)
(let* ((port-in (open-input-file path-in))
(port-out (open-input-file path-out))
(data-in (get-string-all port-in))
(data-out (get-string-all port-out)))
(close port-in)
(close port-out)
(if (diff? data-in data-out
#:allow (lambda (a b)
(let* ((rx '(or (seq bos "#")
(seq bos
"KEYWORDS=\""
(+ (or alphanumeric whitespace #\- #\~))
"\""
eos)))
(match-a (irregex-search rx a))
(match-b (irregex-search rx b)))
(if (and match-a match-b)
#false #true))))
(let ((output-port (open-output-file
(string-join (list folder-out
(ebuild-name (1+ rel-out)))
file-name-separator-string))))
(display data-in output-port)
(close output-port)
(string-join (list folder-out
(ebuild-name (1+ rel-out)))
file-name-separator-string))
(if (diff? data-in data-out
#:allow (lambda (a b)
(let* ((rx '(seq bos
"KEYWORDS=\""
(+ (or alphanumeric whitespace #\- #\~))
"\""
eos))
(match-a (irregex-search rx a))
(match-b (irregex-search rx b)))
(if (and match-a match-b)
#false #true))))
(let ((output-port (open-output-file
(string-join (list folder-out
(ebuild-name rel-out))
file-name-separator-string))))
(display data-in output-port)
(close output-port)
(string-join (list folder-out
(ebuild-name rel-out))
file-name-separator-string))
#false)))
#false))
#false))))
;;Procedure to generate the required ebuild from the given releases.
(define-public ebuild-gen
(lambda* (parms releases #:key
(version-components (lambda (version)
(map (lambda (component)
(string->number component))
(string-split version #\.))))
(keep-components (list 1 1 2))
(keep (lambda (version) #f))
(drop (lambda (version) #f))
(template (string-append (assoc-ref parms 'name) ".tmpl"))
(post-hook (lambda (ebuild vars)
(system* "ebuild"
(string-join (list (assoc-ref parms 'repo)
(assoc-ref parms 'category)
(assoc-ref parms 'name)
ebuild)
file-name-separator-string)
"manifest")
'()))
ignore-autogen-diff)
""
(if (>= (assoc-ref parms 'verbosity) verbosity-notice)
(begin (display "Releases:\n")
(pretty-print releases)))
(letrec* ((version-list
(map (lambda (release)
(list (version-components (assoc-ref release "version"))
(assoc-ref release "version")))
releases))
(selected-versions (ebuild-fkc version-list keep-components))
(distfiles '())
(ebuilds (filter-map
(lambda (vers)
(let* ((vars (append (car (filter (lambda (rel)
(string= (assoc-ref rel "version")
(second vers)))
releases))
parms))
(ebuild-man (ebuild-cp-man vars))
(ebuild-created (if (and (not ebuild-man)
(not (drop (second vers)))
(or (find (lambda (test-vers)
(string= (second test-vers)
(second vers)))
selected-versions)
(keep (second vers))))
(ebuild-from-tmpl
vars
(assoc-ref parms 'verbosity)
#:tmpl template
#:ignore-diff ignore-autogen-diff)
ebuild-man)))
(if ebuild-created
(set! distfiles
(append distfiles
(post-hook ebuild-created vars))))
ebuild-created))
version-list)))
(values ebuilds distfiles))))

317
ebuild/repo.scm Normal file
View file

@ -0,0 +1,317 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; repo.scm
;; Copyright (C) 2025 Cor Legemaat <cor@cor.za.net>
;;
;; This file is part of ebuild-autogen: you can redistribute it and/or modify it
;; under the terms of the GNU Affero General Public License as published by the
;; Free Software Foundation, version 3 of the License.
;;
;; ebuild-autogen is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
;; more details.
;;
;; You should have received a copy of the GNU General Public License along with
;; ebuild-autogen. If not, see <https://www.gnu.org/licenses/>.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-module (ebuild repo)
#:use-module (ebuild defs)
#:use-module (ebuild utils)
#:use-module (ice-9 exceptions)
#:use-module (ice-9 ftw)
#:use-module (ice-9 pretty-print)
#:use-module (ice-9 textual-ports)
#:use-module (srfi srfi-1)
#:use-module (git bindings)
#:use-module (git repository)
#:use-module (git clone)
#:use-module (git submodule)
#:use-module (rx irregex))
;; Required initialization call.
(libgit2-init!)
(define-public repo-root-for
(lambda (path verbosity)
(let ((repo-orig
(with-exception-handler
(lambda (exception)
(display exception) (newline)
(display "error: Failed to find the repository root for \"")
(display path)
(display "\", ebuild-autogen is designed to work with ")
(display "portage overlay repositories in git!") (newline)
(display "If you are testing and don't have a git repository ")
(display "yet for your overlay, make it one by running ")
(display "'git init' in the root of the repository.")
(newline))
(lambda ()
(repository-discover path)))))
(let ((repo-root (dirname repo-orig)))
(if (>= verbosity verbosity-notice)
(begin (display "Using repository: ")
(display repo-root)
(newline)))
(if (not (file-exists? (string-append repo-root
"/autogen/")))
(raise-exception
(make-exception
(make-external-error)
(make-exception-with-message
"Can't find the \"autogen\" folder in repository root")
(make-exception-with-irritants repo-root))))
repo-root))))
(define-public repo-clone
(lambda (url folder)
""
(clone url folder)))
(define-public repo-update-src
(lambda (path)
""
(submodule-update (submodule-lookup (repository-open path)
"autogen"))))
;; TODO guile-git method...
;; https://stackoverflow.com/questions/572549/difference-between-git-add-a-and-git-add
(define-public repo-push-master
(lambda ()
""
(system* "/usr/bin/git" "add" "-A")
(system* "/usr/bin/git" "commit" "-m" "ebuild-autogen update")
(system* "/usr/bin/git" "push" "origin" "master")))
(define-public cp-repo-file
(lambda* (repo folder-dst folder-src file verbosity #:key
(file-dst file)
(sub-folder ""))
""
(let ((file-in (string-join (list repo folder-src file)
file-name-separator-string))
(file-out (string-join (list repo folder-dst file-dst)
file-name-separator-string))
(for-each-file
(lambda (files)
(append-map (lambda (sub-file)
(if (not (char=? (car (string->list sub-file)) #\.))
(cp-repo-file repo
(string-join (list folder-dst file-dst)
file-name-separator-string)
(string-join (list folder-src file)
file-name-separator-string)
sub-file
verbosity
#:sub-folder (string-join (if (string=? sub-folder "")
(list file)
(list sub-folder file))
file-name-separator-string))
'()))
files))))
(if (eq? (stat:type (stat file-in)) 'directory)
(if (file-exists? file-out)
(if (eq? (stat:type (stat file-out)) 'directory)
(for-each-file (scandir file-in))
(begin (if (>= verbosity verbosity-critical)
(begin (display "warning: \"")
(display file-in)
(display "\" not a folder and won't ")
(display "represent autogen source data!")
(newline)))
'()))
(begin (mkdir file-out)
(for-each-file (scandir file-in))))
(begin (if (file-exists? file-out)
(let* ((port-in (open-input-file file-in))
(port-out (open-input-file file-out))
(data-in (get-string-all port-in))
(data-out (get-string-all port-out)))
(close port-in)
(close port-out)
(if (diff? data-in data-out)
(let ((output-port (open-output-file file-out)))
(if (>= verbosity verbosity-warn)
(begin (display "Destination differs for \"")
(display file)
(display "\" overriding.")
(newline)))
(display data-in output-port)
(close output-port))
(list (string-join (if (string=? sub-folder "")
(list file)
(list sub-folder file))
file-name-separator-string))))
(copy-file file-in file-out))
(list (string-join (if (string=? sub-folder "")
(list file)
(list sub-folder file))
file-name-separator-string)))))))
(define-public folder-list
(lambda (path ignore-meta)
""
(filter-map (lambda (entry)
(if (eq? 'directory
(stat:type (stat (string-append path "/" entry))))
(if (and ignore-meta
(or (string=? entry "metadata")
(string=? entry "profiles")))
#f
(if (char=? (car (string->list entry)) #\.)
#f entry))
#f))
(scandir path))))
(define-public build-pkg-list
(lambda (repo folder in-autogen)
(let* ((file-seperator (car (string->list file-name-separator-string)))
(repo-list (string-split repo file-seperator))
(repo-path-len (length repo-list))
(fpl (let ((folder-list (string-split folder file-seperator)))
(if in-autogen
;; In autogen folder, aka the source pkgs.
(if (> (length folder-list) repo-path-len)
(if (string=? (car (take-right folder-list
(- (length folder-list)
repo-path-len)))
"autogen")
folder-list
(append repo-list
(list "autogen")
(take-right folder-list
(- (length folder-list)
repo-path-len))))
(append folder-list (list "autogen")))
;; Outside autogen folder, the generated pkgs.
(if (> (length folder-list) repo-path-len)
(if (string=? (car (take-right folder-list
(- (length folder-list)
repo-path-len)))
"autogen")
(append repo-list
(take-right folder-list
(- (length folder-list)
(1+ repo-path-len))))
folder-list)
folder-list))))
(depth (- (length fpl)
repo-path-len
(if in-autogen 1 0))))
(cond ((= depth 2)
(list (list (cons 'category (first (take-right fpl 2)))
(cons 'name (last fpl)))))
((= depth 1)
(map (lambda (pkg)
(list (cons 'category (last fpl))
(cons 'name pkg)))
(folder-list (string-join fpl file-name-separator-string)
#t)))
((= depth 0)
(if (null? (folder-list (string-join fpl file-name-separator-string)
#t))
(list (list))
(append-map (lambda (cat)
(map (lambda (pkg)
(list (cons 'category cat)
(cons 'name pkg)))
(folder-list (string-join (append fpl
(list cat))
file-name-separator-string)
#f)))
(folder-list (string-join fpl file-name-separator-string) #t))))
(else "")))))
(define-public setup-pkg
(lambda (parms)
""
(let ((src-path (string-join (list (assoc-ref parms 'repo)
"autogen"
(assoc-ref parms 'category)
(assoc-ref parms 'name))
file-name-separator-string))
(dst-path (string-join (list (assoc-ref parms 'repo)
(assoc-ref parms 'category)
(assoc-ref parms 'name))
file-name-separator-string)))
;;Check folder exists.
(if (file-exists? dst-path)
(if (>= (assoc-ref parms 'verbosity) verbosity-notice)
(begin (display "Folder for ")
(display (assoc-ref parms 'category))
(display "/")
(display (assoc-ref parms 'name))
(display " already exist.")
(newline)))
(mkpath dst-path))
;;cp each ebuild and the files folder.
(let ((test (append-map
(lambda (file)
(let ((ret (cp-repo-file
(assoc-ref parms 'repo)
(string-join (list (assoc-ref parms 'category)
(assoc-ref parms 'name))
file-name-separator-string)
(string-join (list "autogen"
(assoc-ref parms 'category)
(assoc-ref parms 'name))
file-name-separator-string)
file
(assoc-ref parms 'verbosity))))
ret))
(filter-map
(lambda (file)
(if (irregex-search '(or (seq bos "files" eos)
(seq ".ebuild" eos ))
file)
file
#f))
(scandir src-path)))))
test)
)))
(define-public clean-files
(lambda (file-list file-folder verbosity)
(letrec* ((for-each-file
(lambda (sub-path)
(append-map
(lambda (file)
(let ((path (string-join (list file-folder sub-path file)
file-name-separator-string)))
(if (not (or (char=? (car (string->list file)) #\.)
(string=? file "Manifest")))
(if (eq? (stat:type (stat path)) 'directory)
(for-each-file (string-join (if (string=? sub-path "")
(list file)
(list sub-path file))
file-name-separator-string))
(list (string-join (if (string=? sub-path "")
(list file)
(list sub-path file))
file-name-separator-string)))
'())))
(scandir (string-join (list file-folder sub-path)
file-name-separator-string))))))
;; (display "file-list:") (newline)
;; (pretty-print file-list)
;; (display "each-file:") (newline)
;; (pretty-print (for-each-file ""))
;; (display "to-clean:") (newline)
;; (pretty-print (filter-map (lambda (file)
;; (if (any (lambda (a)
;; (string=? a file))
;; file-list)
;; #false file))
;; (for-each-file "")))
(for-each (lambda (absolute-file)
(if (>= verbosity verbosity-notice)
(begin (display "Cleaned absolute file \"")
(display absolute-file) (display "\"")
(newline)))
(delete-file (string-join (list file-folder absolute-file)
file-name-separator-string)))
(filter-map (lambda (file)
(if (any (lambda (a)
(string=? a file))
file-list)
#false file))
(for-each-file ""))))))

117
ebuild/utils.scm Normal file
View file

@ -0,0 +1,117 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; utils.scm
;; Copyright (C) 2025 Cor Legemaat <cor@cor.za.net>
;;
;; This file is part of ebuild-autogen: you can redistribute it and/or modify it
;; under the terms of the GNU Affero General Public License as published by the
;; Free Software Foundation, version 3 of the License.
;;
;; ebuild-autogen is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
;; more details.
;;
;; You should have received a copy of the GNU General Public License along with
;; ebuild-autogen. If not, see <https://www.gnu.org/licenses/>.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-module (ebuild utils)
#:use-module (ice-9 ftw)
#:use-module (rx irregex)
#:use-module (srfi srfi-1))
(define-public list->str-list
(lambda* (in-list glue #:key (pre "") (post ""))
""
;;(display "in-list=") (display in-list) (newline)
(if (null? in-list)
""
(string-concatenate
(append (list pre)
(cdr (append-map (lambda (item)
(append (list glue)
(list item)))
in-list))
(list post))))))
(define-public any-str-list
(lambda (test-list val)
""
(any (lambda (test-val)
(if (string=? test-val val)
#t #f))
test-list)))
(define-public diff?
(lambda* (data-a data-b #:key (allow (lambda (a b) #true)))
""
(any (lambda (t) t)
(map (lambda (a b)
(if (string=? a b)
#false
(allow a b)))
(if (string? data-a)
(string-split data-a #\newline)
data-a)
(if (string? data-b)
(string-split data-b #\newline)
data-b)))))
(define-public last-ebuild-rel
(lambda (folder pkg version)
""
(let* ((files (scandir folder))
(releases (filter-map
(lambda (file)
(if (string=? file (string-append pkg
"-"
version
".ebuild"))
0
(let ((my-match (irregex-search
`(seq (look-behind ,pkg
"-"
,version
"-r")
(+ numeric)
(look-ahead ".ebuild"))
file)))
(if my-match
(string->number
(irregex-match-substring my-match))
#false))))
files)))
(cond ((zero? (length releases)) 0)
((<= 2 (length releases)) (car (sort releases >)))
(else (car releases))))))
(define-public mkpath
(lambda (path)
""
(let* ((split-path (filter-map (lambda (str)
(if (string<> str "") str #f))
(string-split path
(car (string->list
file-name-separator-string)))))
(fixed-path (string-append (string-concatenate (map (lambda (folder)
(string-append "/" folder))
split-path))
"/"))
(path-1up (string-concatenate (map (lambda (folder)
(string-append "/" folder))
(reverse (cdr (reverse split-path)))))))
(if (not (access? fixed-path F_OK))
(if (access? path-1up F_OK)
(if (access? path-1up W_OK)
(mkdir fixed-path)
(error (string-append "Error no write permission in \""
path-1up
"\" to create \""
(car (reverse split-path))
"\" folder!")))
(begin (mkpath path-1up)
(mkdir path)))))))
(define-public cmp-str-lists
(lambda (list1 list2)
""
(every string=? list1 list2)))

29
ebuild/version.scm.in Normal file
View file

@ -0,0 +1,29 @@
;; -*- Mode: Scheme; geiser-scheme-implementation: guile; tab-width: 2 -*-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; version.scm
;; Copyright (C) 2025 Cor Legemaat <cor@cor.za.net>
;;
;; This file is part of ebuild-autogen: you can redistribute it and/or modify it
;; under the terms of the GNU Affero General Public License as published by the
;; Free Software Foundation, version 3 of the License.
;;
;; ebuild-autogen is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
;; more details.
;;
;; You should have received a copy of the GNU General Public License along with
;; ebuild-autogen. If not, see <https://www.gnu.org/licenses/>.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-module (ebuild version))
(define-public version-str "{{version}}")
(define-public version (map string->number
(string-split version-str #\.)))
(define-public version-major (list-ref version 0))
(define-public version-minor (list-ref version 1))
(define-public version-release (list-ref version 2))