Initial commit

This commit is contained in:
puck 2024-10-03 23:57:22 +00:00
commit 55a1efa08f
60 changed files with 5485 additions and 0 deletions

22
lang/go/default.nix Normal file
View file

@ -0,0 +1,22 @@
{ chickenPackages, libsodium, callPackage, xxd }:
(callPackage ../../lib/build-chicken-parallel {}) {
name = "zilch-lang-go";
src = ./.;
buildInputs = with chickenPackages.chickenEggs; [
chickenPackages.chicken
r7rs
json
srfi-152
srfi-207
(callPackage ../../core {})
xxd
];
overrides.preBuild = ''
(cat utils/parser/main.go; printf '\0') | xxd -i -n parser_source > parser_source.h
(cat utils/dirhash/main.go; printf '\0') | xxd -i -n dirhash_source > dirhash_source.h
(cat utils/unzip-one/main.go; printf '\0') | xxd -i -n unzip_one_source > unzip_one_source.h
'';
}

153
lang/go/src/core.sld Normal file
View file

@ -0,0 +1,153 @@
(define-library (zilch lang go core)
(import
(scheme base) (scheme write) (scheme process-context) (scheme lazy)
(zilch file) (zilch magic) (zilch nix drv) (zilch nix path)
(zilch nixpkgs) (zilch zexpr)
json
(chicken foreign)
(srfi 4))
(export
build-importcfg
build-embedcfg
rewrite-package-name
%goarch
env-for-goarch
defines-for-goarch
go-compile
go-generate-symabi
go-compile-assembly
go-toolchain)
(begin
(define %goarch (make-parameter "amd64"))
; Import the existing Go from nixpkgs.
(define go-toolchain (cdr (assoc "out" (nixpkgs "go_1_23"))))
;; Builds an importcfg file, containing an alist of packages to .a files,
;; and an alist of package names to actual package names.
;; `++packagefiles++` is a alist of package name to .a file (api type),
;; `++importmap++` is an alist of package name to actual package name (used in cases of e.g. replace)
(define (build-importcfg packagefiles importmap)
(call-with-port (open-output-string)
(lambda (outstr)
(write-string "# import config\n" outstr)
(for-each
(lambda (v)
(write-string "packagefile " outstr)
(write-string (car v) outstr)
(write-char #\= outstr)
(write-string (cdr v) outstr)
(write-char #\newline outstr))
packagefiles)
(for-each
(lambda (v)
(write-string "importmap " outstr)
(write-string (car v) outstr)
(write-char #\= outstr)
(write-string (cdr v) outstr)
(write-char #\newline outstr))
importmap)
(get-output-string outstr))))
;; `++patterns++` is an alist of the pattern used to match files (e.g. `++foo/++`, or `++a.*++`) to a list of filenames.
;; `++files++` is an alist of file name to actual path.
(define (build-embedcfg patterns files)
(call-with-port (open-output-string)
(lambda (outstr)
(json-write
(vector
(cons "Patterns" (list->vector patterns))
(cons "Files" (list->vector files)))
outstr)
(get-output-string outstr))))
; Clean up the package name to use in drv names.
(define (rewrite-package-name name)
(set! name (string-copy name))
(do ((x 0 (+ x 1)))
((>= x (string-length name)) name)
(when (char=? (string-ref name x) #\/) (string-set! name x #\_))
(when (char=? (string-ref name x) #\[) (string-set! name x #\_))
(when (char=? (string-ref name x) #\space) (string-set! name x #\_))
(when (char=? (string-ref name x) #\]) (string-set! name x #\_))))
;; An empty go_asm.h file used to generate symabis.
(define empty-asmhdr (zdir `(("go_asm.h" . ,(zfile "")))))
;; Environment to append to the build environment for Go.
(define (env-for-goarch)
`(("GOARCH" . ,(%goarch))))
;; Extra defines to add to `++go tool asm++` uses.
(define (defines-for-goarch)
`(
"-D" "GOOS_linux"
"-D" ,(string-append "GOARCH_" (%goarch))
,@(if (string=? (%goarch) "amd64") '("-D" "GOAMD64_v1") '())))
;; Returns an alist of three store paths; `++api++` containing the compiler's output,
;; `++code++` containing the linkobj, and `++asmhdr++` containing the headers needed for assembly
;; code to properly use Go functions and variables.
(define (go-compile std package-name importcfg symabis embeds files)
(define args
#~(
,@(if std '("-std") '())
#$@(if symabis `("-symabis" ,#$symabis) '())
#$@(if embeds `("-embedcfg" ,#$embeds) '())
"-buildid" "zilch go-compile" ; this goes into both code and __.PKGDEF, so can't be a reference to the code output, sadly
"-p" #$package-name
"-o" ,(make-placeholder "api")
"-linkobj" ,(make-placeholder "code")
"-importcfg" #$importcfg
"-nolocalimports"
"-asmhdr" ,(make-placeholder "asmhdr")
"-trimpath" ,(apply string-append (map (lambda (f) (string-append (cdr f) "=>" package-name "/" (car f) ";")) #$files))
. ,(map cdr #$files)))
(store-path-for-ca-drv*
(string-append (rewrite-package-name package-name) "-src")
"x86_64-linux"
#~(,(string-append #$go-toolchain "/bin/go") "tool" "compile" . #$args)
(env-for-goarch) '("api" "code" "asmhdr")))
;; Returns a store path containing the symabi for the files provided.
(define (go-generate-symabi package-name include-path files)
(define args
#~(
,@(defines-for-goarch)
"-gensymabis"
"-p" #$package-name
"-o" ,(make-placeholder "symabi")
"-I" ,(string-append #$go-toolchain "/share/go/pkg/include")
"-I" #$empty-asmhdr
,@(if include-path (list "-I" #$include-path) '())
. #$files))
(cdar (store-path-for-ca-drv*
(string-append (rewrite-package-name package-name) "-asm-symabis")
"x86_64-linux"
#~(,(string-append #$go-toolchain "/bin/go") "tool" "asm" . #$args)
(env-for-goarch) '("symabi"))))
;; Returns a store path containing the `++code++` of the provided assembly files.
(define (go-compile-assembly package-name include-path include-path2 files)
(define args
#~(
,@(defines-for-goarch)
"-p" #$package-name
"-o" ,(make-placeholder "code")
"-I" ,(string-append #$go-toolchain "/share/go/pkg/include")
,@(if include-path (list "-I" #$include-path) '())
,@(if include-path2 (list "-I" #$include-path2) '())
"-trimpath" ,(apply string-append (map (lambda (f) (string-append (cdr f) "=>" package-name "/" (car f) ";")) #$files))
. ,(map cdr #$files)))
(cdar (store-path-for-ca-drv*
(string-append (rewrite-package-name package-name) "-asm")
"x86_64-linux"
#~(,(string-append #$go-toolchain "/bin/go") "tool" "asm" . #$args)
(env-for-goarch) '("code"))))))

33
lang/go/src/fetch.sld Normal file
View file

@ -0,0 +1,33 @@
(define-library (zilch lang go fetch)
(import
(scheme base) (scheme write) (scheme read) (scheme file) (scheme char)
(zilch magic) (zilch zexpr)
(zilch nixpkgs)
(chicken format))
(export
fetch-with-known-url rewrite-module-name-for-url)
(begin
(define fetch-cache (call-with-input-file "/home/.zilchcache" read))
(define (fetch-with-known-url name url)
(define cache-entry (assoc url fetch-cache))
(define hash (if cache-entry
(cdr cache-entry)
(begin (printf "..fetching ~S ~S\n" name url)
(nix-prefetch-url name url #f))))
(unless cache-entry
(set! fetch-cache (cons (cons url hash) fetch-cache))
(call-with-output-file "/home/.zilchcache" (lambda (out) (write fetch-cache out))))
(store-path-for-fod name "builtin" '("builtin:fetchurl") `(("url" . ,url) ("outputHashMode" . "flat")) "sha256" hash #f))
(define (rewrite-module-name-for-url name)
(define out "")
(string-for-each
(lambda (ch)
(if
(char-upper-case? ch)
(set! out (string-append out (string #\! (char-downcase ch))))
(set! out (string-append out (string ch)))))
name)
out)))

124
lang/go/src/go.sld Normal file
View file

@ -0,0 +1,124 @@
(define-library (zilch lang go)
(import
(scheme base) (scheme write) (scheme process-context) (scheme lazy)
(chicken file) (chicken format)
(zilch magic) (zilch file) (zilch zexpr)
(zilch nix drv) (zilch nix path) (zilch nixpkgs)
(json)
(chicken base) (chicken format) (chicken foreign)
(srfi-4)
(zilch lang go core))
(export
make-go-package go-package?
go-package-name go-package-import-path
go-package-api go-package-code go-package-dependencies
go-dependency-closure
go-package-compile go-package-link)
(begin
;; A go package consists of a few separate `++(zilch magic)++` store paths.
;; The `++name++` is the package name as compiled, and `++import-path++` is a nicer
;; package name for "main" packages. The `++go-package-api++` is a store path consisting
;; of a `++.a++` containing the output of the compiler's `++__.PKGDEF++` only, which
;; contains the exported types and functions, along with a slight amount of LTO and
;; inlining metadata. the `++go-package-code++` store path contains the actual assembly
;; of the package.
(define-record-type <go-package>
(make-go-package name import-path api code dependencies)
go-package?
(name go-package-name)
(import-path go-package-import-path)
(api go-package-api)
(code go-package-code)
(dependencies go-package-dependencies))
(define-record-printer (<go-package> pkg out)
(fprintf out "#<go-package ~A api: ~S code: ~S deps: ~S>"
(if (string=? (go-package-import-path pkg) (go-package-name pkg))
(go-package-name pkg)
(string-append (go-package-name pkg) " (" (go-package-import-path pkg) ")"))
(go-package-api pkg)
(go-package-code pkg)
(map go-package-name (go-package-dependencies pkg))))
;; Recursively walk over the dependencies of a `++go-package++`, prepending to the `++vals++` list.
(define (go-dependency-closure package vals)
(unless (member package vals)
(set! vals (cons package vals))
(for-each
(lambda (pkg)
(set! vals (go-dependency-closure pkg vals)))
(go-package-dependencies package)))
vals)
;; `(go-package-compile name deps source-files)`
;; or `(go-package-compile name path deps source-files assembly-files assembly-includes embed-filenames embed-patterns)`
;; Build a Zilch-defined Go package of one store path as source code, and a list of dependencies.
(define go-package-compile
(case-lambda
((name deps source-files) (go-package-compile name name deps source-files '() '() '() '()))
((name path deps source-files assembly-files assembly-includes embed-filenames embed-patterns)
(define api-importcfg
(zfile #~,(build-importcfg #$(map (lambda (pkg) (cons (go-package-import-path pkg) (go-package-api pkg))) deps) '())))
(define api-embedcfg
(zfile #~,(build-embedcfg #$embed-patterns #$embed-filenames)))
(define symabis #f)
(unless assembly-files (set! assembly-files '()))
(define path-or-name (if (string=? name "main") name path))
(define assembly-includes-dir
(if (list? assembly-includes)
(zdir (map (lambda (pair) (cons (car pair) (zsymlink (cdr pair)))) assembly-includes))
assembly-includes))
(unless (eq? assembly-files '())
(set! symabis (go-generate-symabi path-or-name assembly-includes-dir #~,(map cdr #$assembly-files))))
(define compiled-go
(go-compile #f path-or-name api-importcfg symabis api-embedcfg source-files))
(define merged-asmhdr
(zdir "go_asm.h" (zsymlink (cdr (assoc "asmhdr" compiled-go)))))
;; ISSUE: this needs the source dir for assembly imports reasons (filter out .h files?)
(define compiled-assembly
(map
(lambda (f) (go-compile-assembly path-or-name assembly-includes-dir merged-asmhdr (list f)))
assembly-files))
; Assembly code doesn't have an API, so use the Go code's API only.
(define go-api (cdr (assoc "api" compiled-go)))
; Make a list of the "code" output from the Go with the compiled assembly files.
; NOTE: .go has to be compiled in one go; but .s is compiled one file at a time.
(define all-code (cons (cdr (assoc "code" compiled-go)) compiled-assembly))
; (printf " -> (store-path-for-ca-drv* meow meow ~S ~S meow)\n" all-code (env-for-goarch))
; Use `go tool pack` to merge the code together.
(define merged-code
(if (length assembly-files)
(cdar (store-path-for-ca-drv*
(string-append "go-" (rewrite-package-name path) "-code") "x86_64-linux"
#~(,(string-append #$go-toolchain "/bin/go") "tool" "pack" "c" ,(make-placeholder "code") . #$all-code)
(env-for-goarch)
'("code")))
(cdr (assoc "code" compiled-go))))
; (printf " -> (make-go-package ~S ~S ~S ~S ~S)\n" name path (cdr (assoc "api" compiled-go)) merged-code deps)
(make-go-package name path (cdr (assoc "api" compiled-go)) merged-code deps))))
;; Link a `++go-package++` into a binary that can be (statically) executed.
(define (go-package-link pkg)
(define code-importcfg
(zfile #~,(build-importcfg #$(map (lambda (pkg) (cons (go-package-import-path pkg) (go-package-code pkg))) (go-dependency-closure pkg '())) '())))
(cdar (store-path-for-ca-drv* (rewrite-package-name (go-package-import-path pkg)) "x86_64-linux"
#~(,(string-append #$go-toolchain "/bin/go") "tool" "link" "-buildid" ,(string-append "zilch out=" (make-placeholder "out")) "-importcfg" #$code-importcfg "-o" ,(make-placeholder "out") #$(go-package-code pkg)) (env-for-goarch) '("out"))))))

230
lang/go/src/mod.sld Normal file
View file

@ -0,0 +1,230 @@
;; Processes go modules.
(define-library (zilch lang go mod)
(import
(scheme base) (scheme write) (scheme read) (scheme file) (scheme process-context) (scheme lazy) (scheme case-lambda)
(chicken file)
(zilch magic) (zilch file) (zilch zexpr)
(zilch nix drv) (zilch nix path) (zilch nixpkgs)
(json)
(chicken base) (chicken format) (chicken foreign)
(scheme char)
(srfi 4) (srfi 128) (srfi 132) (srfi 133) (srfi 146) (srfi 152) (srfi 207)
(zilch lang go) (zilch lang go core) (zilch lang go stdlib) (zilch lang go vfs) (zilch lang go sum) (zilch lang go fetch) (zilch lang go package)
(zilch lang go version)
(chicken foreign))
(export
collect-requirements-for-module collect-packages-from-requires)
(begin
(define (is-builtin str)
(not (string-index str (lambda (ch) (char=? ch #\.)))))
(define (filter proc lis)
(cond
((eq? lis '()) lis)
((proc (car lis))
(cons (car lis) (filter proc (cdr lis))))
(else (filter proc (cdr lis)))))
;; Read a go.mod file. This returns a processed json object, like `go mod edit -json` outputs by default.
(define (read-go-mod mod-file)
(call-with-port
;; TODO(puck): don't use /bin/sh here.
(store-path-open (cdar (store-path-for-ca-drv* "go.mod.json" "x86_64-linux" #~("/bin/sh" "-c" ,(string-append #$go-toolchain "/bin/go mod edit -json " #$mod-file " > $out")) '() '("out"))))
(lambda (p) (json-read p))))
(define (vector-get-kv-value key vec)
(vector-any (lambda (v) (and (string=? (car v) key) (cdr v))) vec))
;; Reads in the module rooted by the vfs, and resolves its requirements list.
;; This returns two values: the name of the root module, and a mapping of module name to a pair of its version and the vfs.
(define (collect-requirements-for-module vfs replaces)
(define sum-lines '())
(define (parse-sumfile go-sum)
(vector-for-each
(lambda (line)
(define loc (assoc (go-sum-module line) sum-lines))
(unless (go-sum-path line)
(unless loc
(set! sum-lines (cons (list (go-sum-module line)) sum-lines))
(set! loc (car sum-lines)))
(unless (assoc (go-sum-version line) (cdr loc))
(set-cdr! loc (cons (cons (go-sum-version line) (delay (vfs-from-dirhash line))) (cdr loc))))))
go-sum))
(define (add-modules-from-sum nvfs dir)
(define sumfile (vector-get-kv-value "go.sum" (vector-get-kv-value dir nvfs)))
(when sumfile
(parse-sumfile (call-with-port (store-path-open sumfile) parse-go-sum-file))))
(define collected-requires (mapping (make-default-comparator)))
(fprintf (current-error-port) "Collecting required modules\n")
(define (find-requires-from-mod vfs dir)
(define modfile (vector-get-kv-value "go.mod" (vector-get-kv-value dir vfs)))
(define pathname #f)
(when modfile
(let*
((go-mod (read-go-mod modfile))
(module-obj (vector-get-kv-value "Module" go-mod))
(path (vector-get-kv-value "Path" module-obj))
(require-data (vector-get-kv-value "Require" go-mod))
(require-list (if (list? require-data) require-data '())))
(fprintf (current-error-port) "- found ~S (requires ~S modules)\n" path (length require-list))
(for-each
(lambda (req)
(define require-path (vector-get-kv-value "Path" req))
(define require-version (vector-get-kv-value "Version" req))
(define dep (mapping-ref/default collected-requires require-path #f))
(unless dep (set! dep (cons require-version #f))
(set! collected-requires (mapping-set! collected-requires require-path dep)))
(when (and (car dep) (version<? (car dep) require-version))
(set-car! dep require-version)
(set-cdr! dep #f)))
require-list)
(set! pathname path)))
pathname)
(define root-path-name #f)
(define (handle-vfs nvfs)
(add-modules-from-sum nvfs "/")
(find-requires-from-mod nvfs "/"))
(for-each
(lambda (vfs)
(define path-name (handle-vfs vfs))
(set! collected-requires (mapping-set! collected-requires path-name (cons #f vfs))))
replaces)
; we have the right module versions and their files now. Iterate over the packages we have,
; until we have none left that need iterating. Once that's done, iterate all the packages and fetch the go.sum for them.
(define (tick)
(define found-valid #f)
(define found-missing '())
(define-values (keys values) (mapping-entries collected-requires))
(for-each
(lambda (key value)
(unless (cdr value)
(let* ((module-data (assoc key sum-lines))
(vfs-for-version (and module-data (assoc (car value) (cdr module-data)))))
(if vfs-for-version
(begin
(handle-vfs (force (cdr vfs-for-version)))
(set-cdr! value (force (cdr vfs-for-version)))
(set! found-valid #t))
(begin
(set! found-missing (cons (cons key (car value)) found-missing)))))))
keys values)
(cond
((and found-valid (not (eqv? found-missing '())))
(tick))
((and found-valid (eqv? found-missing '()))
(tick))
((and (not found-valid) (eqv? found-missing '()))
#f)
((and (not found-valid) (not (eqv? found-missing '())))
(for-each
(lambda (pair)
(fprintf (current-error-port) " (fetching go.sum from sumdb for ~S ~S)\n" (car pair) (cdr pair))
(let ((file (fetch-with-known-url "go.sum" (string-append "https://sum.golang.org/lookup/" (rewrite-module-name-for-url (car pair)) "@" (cdr pair)))))
(call-with-port (store-path-open file) (lambda (port) (read-line port) (parse-sumfile (vector (parse-go-sum-line (read-line port)) (parse-go-sum-line (read-line port))))))))
found-missing)
(tick))))
(set! root-path-name (handle-vfs vfs))
(set! collected-requires (mapping-set! collected-requires root-path-name (cons #f vfs)))
(tick)
(values root-path-name collected-requires))
;; Processes a mapping of module name to a pair of version and vfs, and returns a procedure that takes a package name and returns its go-package.
(define (collect-packages-from-requires collected-requires)
(define (process-package vfs last-part full-path pairs headers)
(define name (cdr (assoc "name" pairs)))
(define go-files (cdr (assoc "goFiles" pairs)))
(define s-files (if (and (assoc "sFiles" pairs) (list? (cdr (assoc "sFiles" pairs)))) (cdr (assoc "sFiles" pairs)) '()))
(define imports (cdr (assoc "imports" pairs)))
; format: ((pattern . (fname file dir)))
(define embed-vectors (cdr (assoc "embeds" pairs)))
(define embeds (if (vector? embed-vectors) (vector->list embed-vectors) '()))
(define embed-filenames '())
(define embed-patterns (map (lambda (pattern) (cons (car pattern) (if (list? (cdr pattern)) (map car (cdr pattern)) '()))) embeds))
(for-each
(lambda (pattern)
(when (list? pattern)
(for-each
(lambda (fname)
(define new-fname (cadr fname))
(if (string=? "//" new-fname) (set! new-fname "/"))
(define dir (vector-get-kv-value new-fname vfs))
(define file-obj (vector-get-kv-value (car (cddr fname)) dir))
(unless (assoc (car fname) embed-filenames) (set! embed-filenames (cons (cons (car fname) file-obj) embed-filenames))))
(cdr pattern))))
embeds)
(define vfsdir (vector-get-kv-value last-part vfs))
(let
((collected-files (map (lambda (name) (cons name (vector-get-kv-value name vfsdir))) go-files))
(collected-assembly-files (map (lambda (name) (cons name (vector-get-kv-value name vfsdir))) s-files))
(collected-assembly-includes (if (= (length s-files) 0) '() #~,(string-append #$(force headers) last-part)))
(collected-imports (map (lambda (name) (if (is-builtin name) (go-stdlib-ref name) (find-package name)))
(filter (lambda (name) (not (member name '("builtin" "unsafe")))) imports))))
(go-package-compile name full-path collected-imports collected-files collected-assembly-files collected-assembly-includes embed-filenames embed-patterns)))
(define packages (mapping (make-default-comparator)))
(define (process-packages-for-module root-path vfs)
(define (dir-has-valid-contents contents)
(vector-any
(lambda (entry)
(or
(eq? (car entry) "go.mod")
(string-suffix? ".go" (car entry))))
contents))
(define module-packages
(find-packages-inside-vfs
(vector-map
(lambda (pair)
(cons
(car pair)
(if (dir-has-valid-contents (cdr pair))
(cdr pair)
(vector-map (lambda (pair) (cons (car pair) "/dev/null")) (cdr pair)))))
vfs)))
(define headers (delay (vfs-to-store (filter-vfs vfs (lambda (dir fname) (string-suffix? ".h" fname))))))
(vector-for-each
(lambda (pair)
(define full-path (string-append root-path (car pair)))
(set! full-path (string-copy full-path 0 (- (string-length full-path) 1)))
(define pairs (vector->list (cdr pair)))
(set! packages (mapping-set! packages full-path (delay (process-package vfs (car pair) full-path pairs headers)))))
module-packages))
(define (find-longest-prefix name)
(define prefixes (mapping-entries (mapping-filter (lambda (key value) (string-prefix? key name)) collected-requires)))
(unless (eq? prefixes '())
(set! prefixes (list-sort! (lambda (left right) (> (string-length left) (string-length right))) prefixes)))
(if (eq? prefixes '())
#f
(car prefixes)))
(define (find-package full-path)
(define package (mapping-ref/default packages full-path #f))
(unless package
(let ((module-key (find-longest-prefix full-path)))
(when module-key
(process-packages-for-module module-key (cdr (mapping-ref/default collected-requires module-key #f)))
(set! package (mapping-ref/default packages full-path #f)))))
(unless package
(error (string-append "Could not find package " full-path)))
(force package))
(define (find-packages-for-module pkg)
(define modules (mapping-filter (lambda (k v) (string-prefix? pkg k)) packages))
(when (= (mapping-size modules) 0)
(let ((module-key (find-longest-prefix pkg)))
(when module-key
(process-packages-for-module module-key (cdr (mapping-ref/default collected-requires module-key #f)))))
(set! modules (mapping-filter (lambda (k v) (string-prefix? pkg k)) packages)))
(mapping-keys modules))
(values find-package find-packages-for-module))))

30
lang/go/src/package.sld Normal file
View file

@ -0,0 +1,30 @@
(define-library (zilch lang go package)
(import
(scheme base)
(zilch file) (zilch magic)
(zilch nixpkgs) (zilch zexpr)
json
(chicken foreign)
(zilch lang go core) (zilch lang go) (zilch lang go vfs) (zilch lang go stdlib))
(export find-packages-inside-vfs)
(begin
(foreign-declare "#include \"parser_source.h\"")
(define go-import-parser
(go-package-link
(go-package-compile "main"
(map go-stdlib-ref '("encoding/json" "fmt" "go/build" "io" "io/fs" "os" "path" "path/filepath" "sort" "strings" "time"))
(list (cons "main.go" (zfile (foreign-value "parser_source" nonnull-c-string)))))))
(define (find-packages-inside-vfs vfs)
(define input
#~,(call-with-port
(open-output-bytevector)
(lambda (bv)
(json-write (vector (cons "GOARCH" (%goarch)) (cons "GOOS" "linux") (cons "files" #$(filter-vfs-for-package-reading vfs))) bv)
(get-output-bytevector bv))))
(define input-file (zfile input))
(define store-path (cdar (store-path-for-ca-drv* "find-packages" "x86_64-linux" #~(#$go-import-parser #$input-file) '() '("out"))))
(call-with-port (store-path-open store-path)
(lambda (p) (json-read p))))))

159
lang/go/src/stdlib.sld Normal file
View file

@ -0,0 +1,159 @@
(define-library (zilch lang go stdlib)
(import
(scheme base) (scheme file) (scheme write) (scheme process-context) (scheme lazy)
(chicken file) (chicken format)
(zilch magic) (zilch file) (zilch zexpr)
(zilch nix drv) (zilch nix path) (zilch nixpkgs)
(json)
(chicken foreign)
(srfi-4)
(zilch lang go core)
(zilch lang go))
(export
go-stdlib-ref)
(begin
;; Helper to read JSON objects until EOF.
(define (read-all-objects port out)
(if (eof-object? (peek-char port))
out
(read-all-objects port (cons (json-read port) out))))
;; Runs `++go list++` (thru `++/bin/sh++`) and reads (IFD) the output to fetch the metadata of the Go standard library and commands.
(define stdlib-objects
(map vector->list
(call-with-port
(store-path-open
(cdar (store-path-for-ca-drv*
"stdenv"
"x86_64-linux"
#~("/bin/sh" "-c" ,(string-append "GOCACHE=$TMPDIR/go-cache " #$go-toolchain "/bin/go list -json -deps std cmd > $out"))
(env-for-goarch) '("out"))))
(lambda (port) (read-all-objects port '())))))
(define (assoc-or-empty name obj)
(define res (assoc name obj))
(if res (cdr res) '()))
;; Extract everything until the first space.
;; Space characters are illegal in Go package names, and `++go list -json std++`
;; uses it to disambiguate multiple versions of some internal packages.
(define (strip-space-bits name)
(do
((x 0 (+ 1 x)))
((or
(>= x (string-length name))
(char=? (string-ref name x) #\space))
(if (>= x (string-length name))
name
(substring name 0 x)))))
;; Tail-recursively remove any packages that, if ignoring the postfixed origin
;; (e.g. `++unsafe [cmd/compile]++`), match either `++unsafe++` or `++builtin++`;
;; these have no source code and are compiler-internal.
(define (remove-builtin-packages pkgs)
(if (eq? pkgs '())
'()
(let ((stripped (strip-space-bits (car pkgs))))
(if (or (string=? stripped "unsafe") (string=? stripped "builtin"))
(remove-builtin-packages (cdr pkgs))
(cons (car pkgs) (remove-builtin-packages (cdr pkgs)))))))
(define (starts-with left right)
(and
(>= (string-length right) (string-length left))
(string=? left (string-copy right 0 (string-length left)))))
(define (filter condition lst)
(if (eq? lst '())
'()
(if (condition (car lst))
(cons (car lst) (filter condition (cdr lst)))
(filter condition (cdr lst)))))
;; Helper that parses the JSON returned by `++go list -json -deps++` and builds a `++go-package++` record.
;; This is distinct from `go-package-compile` because of format differences,
(define (make-stdlib-inner meta)
(define files (assoc-or-empty "GoFiles" meta)) ; .go files
(define sfiles (assoc-or-empty "SFiles" meta)) ; .s files
(define imports (assoc-or-empty "Imports" meta)) ; imports (denormalised)
(define importmap (assoc-or-empty "ImportMap" meta)) ; import map
; Rewrite the import map to be normalised; we use the normalised import path later on.
(when (vector? importmap)
(set! importmap (map (lambda (v) (cons (car v) (strip-space-bits (cdr v)))) (vector->list importmap))))
(define import-path (cdr (assoc "ImportPath" meta)))
(define package-name (cdr (assoc "Name" meta)))
(define name (strip-space-bits import-path))
; Deal with commands properly. (Their package name is "main", but we track import path in other cases)
(when (string=? package-name "main")
(set! name package-name))
(define dir (cdr (assoc "Dir" meta)))
; Fetch dependencies from the rest of the stdlib data.
; We only need the `++api++` at this point.
(define resolved-imports (map (lambda (v) (cons (strip-space-bits v) (go-package-api (go-stdlib-ref v)))) (remove-builtin-packages imports)))
; The importcfg encodes the list of (direct) dependencies. Generate this from the "Imports" entry in the `go list -json` output.
; This uses a workaround for fetchurl behavior having been changed.
(define importcfg (zfile #~,(build-importcfg #$resolved-imports importmap)))
; If this package uses embeds, process them.
(define embed-patterns (assoc-or-empty "EmbedPatterns" meta))
(define embed-files (assoc-or-empty "EmbedFiles" meta))
; alist of (<embed pattern>. values)
(define embedprocessed (map (lambda (l) (cons (strip-space-bits l) (map strip-space-bits (filter (lambda (v) (starts-with l v)) embed-files)))) embed-patterns))
(define embeds #f)
(unless (eq? embed-files '())
(set! embeds
(zfile #~,(build-embedcfg embedprocessed (map (lambda (k) (cons k (string-append dir "/" k))) embed-files)))))
; When compiling assembly code, we first need to generate the symabi; then compile the Go code using that,
; and use the go_asm.h output from the Go compilation to compile the rest of the assembly.
(define symabis #f)
(unless (eq? sfiles '())
(set! symabis (go-generate-symabi name dir (map (lambda (f) (string-append dir "/" f)) sfiles))))
; Compile the go code. Currently done in one single go, rather than per-file; this is a TODO.
(define compiled-go (go-compile #t name importcfg symabis embeds (map (lambda (f) (cons f (string-append dir "/" f))) files)))
(define asmhdrs (cdr (assoc "asmhdr" compiled-go)))
; Move the asmhdr output to the right path for the assembly.
; TODO: use zfile logic, once this works again.
(define merged-asmhdr
(zdir "go_asm.h" (zsymlink asmhdrs)))
; Now compile every assembly file, in order.
(define compiled-assembly
(map
(lambda (f) (go-compile-assembly name dir merged-asmhdr (list (cons f (string-append dir "/" f)))))
sfiles))
; Assembly code doesn't have an API, so use the Go code's API only.
(define go-api (cdr (assoc "api" compiled-go)))
; Make a list of the "code" output from the Go with the compiled assembly files.
; NOTE: .go has to be compiled in one go; but .s is compiled one file at a time.
(define all-code (cons (cdr (assoc "code" compiled-go)) compiled-assembly))
; Use `go tool pack` to merge the code together.
(define merged-code
(cdar
(store-path-for-ca-drv*
(string-append "go-" (rewrite-package-name name) "-code") "x86_64-linux"
#~(,(string-append #$go-toolchain "/bin/go") "tool" "pack" "c" ,(make-placeholder "code") . #$all-code)
(env-for-goarch)
'("code"))))
(make-go-package name import-path go-api merged-code (map go-stdlib-ref (remove-builtin-packages imports))))
; Each entry is a list (name metadata (api code)).
; Use `++delay++` to resolve the DAG lazily on use.
(define stdlib-data (map (lambda (v) (list (cdr (assoc "ImportPath" v)) v (delay (make-stdlib-inner v)))) stdlib-objects))
;; Wrapper that forces evaluation of the promise fetching from a Go stdlib entry.
(define (go-stdlib-ref name)
(define entry (assoc name stdlib-data))
(unless entry (error (string-append "Could not find package " name " in stdlib")))
(force (list-ref entry 2)))))

60
lang/go/src/sum.sld Normal file
View file

@ -0,0 +1,60 @@
(define-library (zilch lang go sum)
(import
(scheme base) (scheme write) (scheme read) (scheme file) (scheme process-context) (scheme lazy) (scheme case-lambda)
(chicken file)
(zilch magic) (zilch file) (zilch zexpr)
(zilch nix drv) (zilch nix path) (zilch nixpkgs)
(json)
(chicken base) (chicken format) (chicken foreign)
(scheme char)
(srfi 4) (srfi 128) (srfi 146) (srfi 207)
(chicken foreign))
(export
parse-go-sum-line parse-go-sum-file go-sum-line? go-sum-module go-sum-version go-sum-path go-sum-hash)
(begin
(define-record-type <go-sum-line>
(make-go-sum-line module version path hash)
go-sum-line?
(module go-sum-module)
(version go-sum-version)
(path go-sum-path)
(hash go-sum-hash))
(define-record-printer (<go-sum-line> sum out)
(fprintf out "#<go-sum ~A ~A~A h1:~A>"
(go-sum-module sum)
(go-sum-version sum)
(if (go-sum-path sum) (go-sum-path sum) "")
(bytevector->base64 (go-sum-hash sum))))
(define (string-find str index char)
(cond
((= index (string-length str)) #f)
((char=? (string-ref str index) char) index)
(else (string-find str (+ index 1) char))))
(define (parse-go-sum-line line)
(define version-space-index (string-find line 0 #\space))
(unless version-space-index (error "go.sum line contains no space characters"))
(define hash-space-index (string-find line (+ version-space-index 1) #\space))
(unless hash-space-index (error "go.sum line contains only one space character"))
(when (string-find line (+ hash-space-index 1) #\space) (error "go.sum line contains too many space characters"))
(define module-path (string-copy line 0 version-space-index))
(define version (string-copy line (+ version-space-index 1) hash-space-index))
(define hash (string-copy line (+ hash-space-index 1)))
(unless (string=? (string-copy hash 0 3) "h1:") (error "go.sum line has invalid algorithm for hash" hash))
(define path #f)
(define path-index (string-find version 0 #\/))
(when path-index
(set! path (string-copy version path-index))
(set! version (string-copy version 0 path-index)))
(make-go-sum-line module-path version path (base64->bytevector (string-copy hash 3))))
(define (parse-go-sum-file port)
(do ((parsed '())
(line "" (read-line port)))
((eof-object? line) (list->vector (reverse parsed)))
(unless (string=? line "") (set! parsed (cons (parse-go-sum-line line) parsed)))))))

46
lang/go/src/version.sld Normal file
View file

@ -0,0 +1,46 @@
(define-library (zilch lang go version)
(import
(scheme base) (srfi 152))
(export parse-version version<?)
(begin
(define (parse-version vstr)
(unless (char=? (string-ref vstr 0) #\v) (error "not a valid version" vstr))
(define first-period (string-index vstr (lambda (ch) (char=? ch #\.)) 1))
(define second-period (string-index vstr (lambda (ch) (char=? ch #\.)) (+ 1 first-period)))
(define prerelease-dash (string-index vstr (lambda (ch) (char=? ch #\-)) (+ 1 second-period)))
(define build-dash (string-index vstr (lambda (ch) (char=? ch #\+)) (+ 1 (or prerelease-dash second-period))))
(define major (string->number (string-copy vstr 1 first-period)))
(define minor (string->number (string-copy vstr (+ first-period 1) second-period)))
(define patch (string->number (string-copy vstr (+ second-period 1) (or prerelease-dash build-dash (string-length vstr)))))
(define prerelease (and prerelease-dash (string-copy vstr (+ prerelease-dash 1) (or build-dash (string-length vstr)))))
(define build (and build-dash (string-copy vstr (+ build-dash 1))))
(list major minor patch prerelease build))
(define (version<? left right)
(set! left (parse-version left))
(set! right (parse-version right))
(or
; left.major < right.major, or
(< (list-ref left 0) (list-ref right 0))
(and
; left.major = right.major, and
(= (list-ref left 0) (list-ref right 0))
(or
; left.minor < right.minor, or
(< (list-ref left 1) (list-ref right 1))
(and
; left.minor = right.minor, and
(= (list-ref left 1) (list-ref right 1))
(or
; left.patch < right.patch, or
(< (list-ref left 2) (list-ref right 2))
(and
; left.patch = right.patch, and
(= (list-ref left 2) (list-ref right 2))
(or
; left has prerelease, right doesn't
(and (list-ref left 3) (not (list-ref right 3)))
; or both have a prerelease and it's comparable
(and (list-ref left 3) (string<? (list-ref left 3) (list-ref right 3)))))))))))))

151
lang/go/src/vfs.sld Normal file
View file

@ -0,0 +1,151 @@
(define-library (zilch lang go vfs)
(import
(scheme base) (scheme write) (scheme read) (scheme file) (scheme process-context) (scheme lazy) (scheme case-lambda)
(chicken file)
(zilch magic) (zilch file) (zilch zexpr)
(zilch nix drv) (zilch nix path) (zilch nixpkgs)
(json)
(chicken base) (chicken format) (chicken foreign)
(scheme char)
(srfi 4) (srfi 128) (srfi 133) (srfi 146) (srfi 152) (srfi 207)
(zilch lang go) (zilch lang go core) (zilch lang go stdlib) (zilch lang go sum) (zilch lang go fetch)
(chicken foreign))
(export vfs-from-dirhash vfs-from-directory filter-vfs filter-vfs-for-package-reading vfs-to-store)
(begin
(define (read-full-file port)
(define buf (make-bytevector 2048 0))
(call-with-port (open-output-bytevector)
(lambda (outport)
(do ((read-bytes 0 (read-bytevector! buf port))) ((eof-object? read-bytes) (get-output-bytevector outport))
(unless (eof-object? read-bytes) (write-bytevector buf outport 0 read-bytes))))))
(foreign-declare "#include \"dirhash_source.h\"")
(define dirhash-generator
(go-package-link
(go-package-compile "main"
(map go-stdlib-ref '("archive/zip" "crypto/sha256" "fmt" "io" "os" "sort"))
(list (cons "main.go" (zfile (foreign-value "dirhash_source" nonnull-c-string)))))))
(foreign-declare "#include \"unzip_one_source.h\"")
(define unpack-zip
(go-package-link
(go-package-compile "main"
(map go-stdlib-ref '("archive/zip" "io" "os"))
(list (cons "main.go" (zfile (foreign-value "unzip_one_source" nonnull-c-string)))))))
(define (rewrite-name name)
(define out "")
(string-for-each (lambda (ch)
(if (char-upper-case? ch) (set! out (string-append out (string #\! (char-downcase ch)))) (set! out (string-append out (string ch))))) name)
out)
(define (vfs-to-store vfs)
(define dirmap (mapping (make-default-comparator)))
(vector-for-each
(lambda (pair)
(define key (car pair))
(define separator (if (string=? key "/") 0 (string-index-right key (lambda (ch) (char=? ch #\/)) 0 (- (string-length key) 1))))
(unless (string=? key "/")
(let
((dirname (string-copy key 0 (+ 1 separator)))
(fname (string-copy key (+ 1 separator) (- (string-length key) 1))))
(set! dirmap (mapping-set! dirmap dirname (cons (cons fname key) (mapping-ref/default dirmap dirname '())))))))
vfs)
(define (translate-dir name)
(define files (vector-any (lambda (f) (and (string=? (car f) name) (cdr f))) vfs))
(define dirs (mapping-ref/default dirmap name '()))
(zdir (append
(map (lambda (kv) (cons (car kv) (zsymlink (cdr kv)))) (vector->list files))
(map (lambda (k) (cons (car k) (translate-dir (cdr k)))) dirs))))
(translate-dir "/"))
(define (fetch-dirhash-for-sum sum-line)
(when (go-sum-path sum-line) (error "go.sum line is invalid for fetch-dirhash-for-sum" sum-line))
(define url (string-append "https://proxy.golang.org/" (rewrite-name (go-sum-module sum-line)) "/@v/" (go-sum-version sum-line) ".zip"))
(define known (fetch-with-known-url "module.zip" url))
(store-path-for-fod "module" "x86_64-linux" #~(#$dirhash-generator) #~(("src" . #$known)) "sha256" (go-sum-hash sum-line) #f))
(define (vfs-from-dirhash sum-line)
(define dirhash-file (fetch-dirhash-for-sum sum-line))
(define url (string-append "https://proxy.golang.org/" (rewrite-name (go-sum-module sum-line)) "/@v/" (go-sum-version sum-line) ".zip"))
(define zip (fetch-with-known-url "module.zip" url))
(define lines '())
(define prefix-length (string-length (string-append (go-sum-module sum-line) "@" (go-sum-version sum-line) "/")))
(call-with-port (store-path-open dirhash-file)
(lambda (port)
(do ((line "" (read-line port)) (hash #f) (file #f))
((eof-object? line) #f)
(unless (string=? "" line)
(set! hash (hex-string->bytevector (string-copy line 0 64)))
(set! file (string-copy line 66))
(set! lines (cons (cons file hash) lines))))))
(define dirs '())
(for-each
(lambda (pair)
(define fpath (string-copy (car pair) prefix-length))
(define slashindex (string-index-right fpath (lambda (c) (char=? c #\/))))
(define dirname "/")
(define filename fpath)
(when slashindex
(set! dirname (string-append "/" (string-copy fpath 0 slashindex) "/"))
(set! filename (string-copy fpath (+ slashindex 1))))
(define dir (assoc dirname dirs))
(define file (store-path-for-fod "file" "x86_64-linux" #~(#$unpack-zip #$zip ,(car pair)) '() "sha256" (cdr pair) #f))
(unless dir
(set! dirs (cons (list dirname) dirs))
(set! dir (car dirs)))
; Skip files we know for sure won't be used.
(unless (or (string-contains dirname "/_") (string-contains dirname "/.") (string-contains dirname "/testdata/") (char=? #\. (string-ref filename 0)) (char=? #\_ (string-ref filename 0)))
(set-cdr! dir (cons (cons filename file) (cdr dir)))))
lines)
(list->vector (map (lambda (pair) (cons (car pair) (list->vector (cdr pair)))) dirs)))
(define (vfs-from-directory osdir)
(define iter-dir #f)
(define output '())
(set! iter-dir
(lambda (dirpath)
(define reldir (string-append osdir "/" dirpath))
(define files '())
(define contents (directory (string-append osdir dirpath)))
(for-each
(lambda (name)
(unless (string=? (string-copy name 0 1) ".")
(if (directory-exists? (string-append reldir "/" name))
(iter-dir (string-append dirpath "/" name))
(set! files (cons (cons name (zfile #~,(call-with-input-file (string-append reldir "/" name) read-full-file))) files)))))
contents)
(set! output (cons (cons (string-append dirpath "/") (list->vector files)) output))))
(iter-dir "")
(list->vector output))
(define (filter-vfs vfs filter)
(vector-map
(lambda (dir)
(cons (car dir)
(vector-map
(lambda (pair)
(if (filter (car dir) (car pair))
(cons (car pair) (cdr pair))
(cons (car pair) "/dev/null")))
(cdr dir))))
vfs))
; List extracted from go src/go/build/build.go.
(define good-extensions '("go" "c" "cc" "cpp" "cxx" "m" "h" "hh" "hpp" "hxx" "f" "F" "for" "f90" "s" "S" "sx" "swig" "swigcxx" "syso"))
(define (extract-extension name i)
(cond ((char=? (string-ref name i) #\.) (string-copy name (+ i 1)))
((= i 0) #f)
(else (extract-extension name (- i 1)))))
(define (filter-vfs-for-package-reading vfs)
(filter-vfs vfs
(lambda (dir fname)
(define extension (extract-extension fname (- (string-length fname) 1)))
(member extension good-extensions))))))

View file

@ -0,0 +1,3 @@
module puck.moe/zilch/go/dirhash
go 1.22.5

View file

@ -0,0 +1,52 @@
package main
import (
"archive/zip"
"crypto/sha256"
"fmt"
"io"
"os"
"sort"
)
func main() {
out, err := os.OpenFile(os.Getenv("out"), os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
panic(err)
}
var files []string
f, err := zip.OpenReader(os.Getenv("src"))
if err != nil {
panic(err)
}
for _, file := range f.File {
if file.Mode().IsDir() {
continue
}
files = append(files, file.Name)
}
sort.Strings(files)
for _, rel := range files {
f, err := f.Open(rel)
if err != nil {
panic(err)
}
defer f.Close()
hash := sha256.New()
_, err = io.Copy(hash, f)
if err != nil {
panic(err)
}
fmt.Fprintf(out, "%x %s\n", hash.Sum(nil), rel)
}
out.Close()
f.Close()
}

View file

@ -0,0 +1,5 @@
module puck.moe/zilch/go/parser
go 1.22.5
require github.com/davecgh/go-spew v1.1.1

View file

@ -0,0 +1,289 @@
package main
import (
"encoding/json"
"fmt"
"go/build"
"io"
"io/fs"
"os"
"path"
"path/filepath"
"sort"
"strings"
"time"
)
type WrappedStat struct {
fs.FileInfo
newName string // meow
}
func (s *WrappedStat) Name() string {
return s.newName
}
type DirStat struct {
name string
}
func (s *DirStat) Name() string {
return s.name
}
func (s *DirStat) Size() int64 {
return 1
}
func (s *DirStat) Mode() fs.FileMode {
return fs.FileMode(fs.ModeDir | 0777)
}
func (s *DirStat) ModTime() time.Time {
return time.Time{}
}
func (s *DirStat) IsDir() bool {
return true
}
func (s *DirStat) Sys() any {
return "zilch"
}
type Input struct {
// directory -> filename -> path
Files map[string]map[string]string `json:"files"`
GOARCH string `json:"GOARCH"`
GOOS string `json:"GOOS"`
}
type Output struct {
Name string `json:"name"`
GoFiles []string `json:"goFiles"`
SFiles []string `json:"sFiles"`
Imports []string `json:"imports"`
Embeds map[string][][]string `json:"embeds"`
}
func main() {
inputFile, err := os.Open(os.Args[1])
if err != nil {
panic(err)
}
var input Input
err = json.NewDecoder(inputFile).Decode(&input)
if err != nil {
panic(err)
}
ctx := build.Context{
GOARCH: input.GOARCH,
GOOS: input.GOOS,
Compiler: "gc",
ToolTags: build.Default.ToolTags,
ReleaseTags: build.Default.ReleaseTags,
ReadDir: func(dir string) ([]fs.FileInfo, error) {
fmt.Printf("ReadDir(%q)\n", dir)
if !strings.HasPrefix(dir, "/code") {
return nil, fs.ErrNotExist
}
dir = path.Clean(dir[5:])
if dir == "." {
dir = "/"
}
if !strings.HasSuffix(dir, "/") {
dir += "/"
}
dircontents, ok := input.Files[dir]
if !ok {
return nil, fs.ErrNotExist
}
infos := make([]fs.FileInfo, len(dircontents))
i := 0
for name, file := range dircontents {
stat, err := os.Stat(file)
if err != nil {
return nil, err
}
infos[i] = &WrappedStat{FileInfo: stat, newName: name}
i = i + 1
}
for key := range input.Files {
if path.Dir(key) == dir {
base := path.Base(key)
if strings.HasPrefix(base, ".") || strings.HasPrefix(base, "_") || base == "testdata" {
continue
}
infos = append(infos, &DirStat{base})
}
}
return infos, nil
},
OpenFile: func(pth string) (io.ReadCloser, error) {
opth := pth
fmt.Printf("OpenFile(%q)\n", pth)
if !strings.HasPrefix(pth, "/code") {
return nil, fs.ErrNotExist
}
pth = path.Clean(pth[5:])
dirname, fname := path.Split(pth)
if dirname == "." {
dirname = "/"
}
dir := input.Files[dirname]
data, err := os.Open(dir[fname])
if err != nil {
return data, fmt.Errorf("OpenFile(%q; %q[%q]; %q): %w", opth, dirname, fname, dir[fname], err)
}
return data, err
},
IsDir: func(dir string) bool {
fmt.Printf("IsDir(%q)\n", dir)
if !strings.HasPrefix(dir, "/code") {
return false
}
dir = path.Clean(dir[5:])
if dir == "." {
dir = "/"
}
if !strings.HasSuffix(dir, "/") {
dir += "/"
}
_, ok := input.Files[dir]
fmt.Printf("IsDir -> %q, %v\n", dir, ok)
return ok
},
HasSubdir: func(root, dir string) (rel string, ok bool) {
root = path.Clean(root)
dir = path.Clean(dir)
return strings.CutPrefix(dir, root)
},
}
var filenames []string
for dirname, files := range input.Files {
for filename := range files {
filenames = append(filenames, filepath.Join(dirname, filename))
}
}
files := make(map[string]Output)
for dir, filelist := range input.Files {
isGo := false
for file := range filelist {
if strings.HasSuffix(file, ".go") {
isGo = true
break
}
}
fmt.Printf("Checking %q..\n", dir)
if strings.Contains(dir, "/.") || strings.Contains(dir, "/_") || strings.Contains(dir, "/testdata/") {
fmt.Printf(" skipping; \n")
continue
}
base := path.Base(dir)
if !isGo || strings.HasPrefix(base, "_") || strings.HasPrefix(base, ".") || base == "testdata" {
fmt.Printf(" skipping (not go)\n")
continue
}
pkg, err := ctx.Import(".", path.Clean("/code"+dir), 0)
if err != nil {
if _, ok := err.(*build.NoGoError); ok {
continue
}
panic(err)
}
out := Output{
Name: pkg.Name,
Imports: pkg.Imports,
GoFiles: pkg.GoFiles,
SFiles: pkg.SFiles,
Embeds: make(map[string][][]string),
}
// _test only, or so
if len(pkg.GoFiles) == 0 {
continue
}
sort.Strings(out.Imports)
sort.Strings(out.GoFiles)
sort.Strings(out.SFiles)
sort.Strings(pkg.EmbedPatterns)
for _, pattern := range pkg.EmbedPatterns {
matchedFiles := []string{}
// TODO: proper matching
if strings.HasPrefix(pattern, "all:") {
pattern = pattern[4:]
}
fullPattern := filepath.Join(dir, pattern)
for _, file := range filenames {
if ok, _ := filepath.Match(fullPattern, file); ok {
matchedFiles = append(matchedFiles, file)
}
}
if len(matchedFiles) == 0 {
dirpattern := fullPattern + "/"
for _, file := range filenames {
if strings.HasPrefix(file, dirpattern) {
matchedFiles = append(matchedFiles, file)
}
if file == fullPattern {
matchedFiles = append(matchedFiles, file)
}
}
}
sort.Strings(matchedFiles)
var split [][]string
for _, match := range matchedFiles {
split = append(split, []string{match[len(dir):], filepath.Dir(match) + "/", filepath.Base(match)})
}
out.Embeds[pattern] = split
}
files[dir] = out
}
out, err := os.OpenFile(os.Getenv("out"), os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
panic(err)
}
if err := json.NewEncoder(out).Encode(files); err != nil {
panic(err)
}
out.Close()
}

View file

@ -0,0 +1,3 @@
module puck.moe/zilch/go/unzip-one
go 1.22.5

View file

@ -0,0 +1,32 @@
package main
import (
"archive/zip"
"io"
"os"
)
func main() {
f, err := zip.OpenReader(os.Args[1])
if err != nil {
panic(err)
}
in, err := f.Open(os.Args[2])
if err != nil {
panic(err)
}
out, err := os.OpenFile(os.Getenv("out"), os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
panic(err)
}
if _, err := io.Copy(out, in); err != nil {
panic(err)
}
out.Close()
in.Close()
f.Close()
}

30
lang/go/zilch-lang-go.egg Normal file
View file

@ -0,0 +1,30 @@
((version "0.0.1")
(synopsis "Nix. Noppes. Nada.")
(author "puck")
(dependencies r7rs json zilch srfi-207)
(component-options
(csc-options "-X" "r7rs" "-X" "zilch.zexpr" "-R" "r7rs" "-optimize-level" "3"))
(components
(extension zilch.lang.go
(source "src/go.sld")
(component-dependencies zilch.lang.go.core))
(extension zilch.lang.go.stdlib
(source "src/stdlib.sld")
(component-dependencies zilch.lang.go.core zilch.lang.go))
(extension zilch.lang.go.mod
(source "src/mod.sld")
(component-dependencies zilch.lang.go.stdlib zilch.lang.go zilch.lang.go.vfs zilch.lang.go.sum zilch.lang.go.fetch zilch.lang.go.package zilch.lang.go.version))
(extension zilch.lang.go.package
(source "src/package.sld")
(component-dependencies zilch.lang.go.stdlib zilch.lang.go zilch.lang.go.vfs))
(extension zilch.lang.go.vfs
(source "src/vfs.sld")
(component-dependencies zilch.lang.go.stdlib zilch.lang.go zilch.lang.go.sum zilch.lang.go.fetch))
(extension zilch.lang.go.sum
(source "src/sum.sld"))
(extension zilch.lang.go.fetch
(source "src/fetch.sld"))
(extension zilch.lang.go.version
(source "src/version.sld"))
(extension zilch.lang.go.core
(source "src/core.sld"))))