zilch/lang/go/src/vfs.sld

124 lines
6.5 KiB
Text
Raw Normal View History

;; Contains go-specific procedures to work with VFSes.
2024-10-03 23:57:22 +00:00
(define-library (zilch lang go vfs)
(import
(scheme base) (scheme write) (scheme file) (scheme process-context) (scheme lazy)
2024-10-03 23:57:22 +00:00
(chicken file)
(zilch magic) (zilch file) (zilch zexpr)
(zilch nix drv) (zilch nix path) (zilch nixpkgs)
(zilch vfs)
2024-10-03 23:57:22 +00:00
(json)
(chicken base) (chicken foreign)
2024-10-03 23:57:22 +00:00
(scheme char)
(srfi 4) (srfi 113) (srfi 128) (srfi 133) (srfi 146) (srfi 152) (srfi 207)
2024-10-03 23:57:22 +00:00
(zilch lang go) (zilch lang go core) (zilch lang go stdlib) (zilch lang go sum) (zilch lang go fetch)
(chicken foreign)
(zilch vfs))
2024-10-03 23:57:22 +00:00
(export vfs-from-dirhash vfs-filter-for-go-package vfs-to-json)
2024-10-03 23:57:22 +00:00
(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
(delay (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))))))))
2024-10-03 23:57:22 +00:00
(foreign-declare "#include \"unzip_one_source.h\"")
(define unpack-zip
(delay (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))))))))
2024-10-03 23:57:22 +00:00
(define (rewrite-go-package-name-for-url name)
2024-10-03 23:57:22 +00:00
(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)
;; Uses the hash in a `<go-sum-line>` to return a store path containing a list of the hashes of each file,
;; following the https://pkg.go.dev/golang.org/x/mod/sumdb/dirhash[`dirhash`] format used by the `go.sum` files.
2024-10-03 23:57:22 +00:00
(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-go-package-name-for-url (go-sum-module sum-line)) "/@v/" (go-sum-version sum-line) ".zip"))
2024-10-03 23:57:22 +00:00
(define known (fetch-with-known-url "module.zip" url))
(store-path-for-fod "module" "x86_64-linux" #~(#$(force dirhash-generator)) #~(("src" . #$known)) "sha256" (go-sum-hash sum-line) #f))
2024-10-03 23:57:22 +00:00
2024-10-04 01:21:07 +00:00
;; Generates a full VFS structure from a module as described by a `go.sum`
;; line. Uses `fetch-dirhash-for-sum` to generate the dirhash, then creates FODs
;; for each file based on said hash; this is then returned as a `<vfs>` structure.
2024-10-03 23:57:22 +00:00
(define (vfs-from-dirhash sum-line)
(define dirhash-file (fetch-dirhash-for-sum sum-line))
(define url (string-append "https://proxy.golang.org/" (rewrite-go-package-name-for-url (go-sum-module sum-line)) "/@v/" (go-sum-version sum-line) ".zip"))
2024-10-03 23:57:22 +00:00
(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 output (mapping (make-default-comparator)))
(define (create-parent-dirs dirname)
(define second-to-last-slash (string-index-right dirname (lambda (c) (char=? c #\/))))
(when second-to-last-slash
(set! output (mapping-set! output (cons (string-copy dirname 0 second-to-last-slash) (string-copy dirname (+ second-to-last-slash 1))) 'directory))
(create-parent-dirs (string-copy dirname 0 second-to-last-slash)))
(unless (or second-to-last-slash (string=? dirname "")) (set! output (mapping-set! output (cons "" dirname) 'directory))))
2024-10-03 23:57:22 +00:00
(for-each
; (path . hash)
2024-10-03 23:57:22 +00:00
(lambda (pair)
(define file-path (string-copy (car pair) prefix-length))
(define hash (cdr pair))
(define last-slash (string-index-right file-path (lambda (c) (char=? c #\/))))
(define dirname "")
(define filename file-path)
(when last-slash
(set! dirname (string-copy file-path 0 last-slash))
(set! filename (string-copy file-path (+ last-slash 1))))
(define file (store-path-for-fod "file" "x86_64-linux" #~(#$(force unpack-zip) #$zip ,(car pair)) '() "sha256" hash #f))
2024-10-03 23:57:22 +00:00
; Skip files we know for sure won't be used.
; TODO(puck): this should be moved to vfs-filter-for-go-package?
2024-10-03 23:57:22 +00:00
(unless (or (string-contains dirname "/_") (string-contains dirname "/.") (string-contains dirname "/testdata/") (char=? #\. (string-ref filename 0)) (char=? #\_ (string-ref filename 0)))
(create-parent-dirs dirname)
(set! output (mapping-set! output (cons dirname filename) file))))
2024-10-03 23:57:22 +00:00
lines)
(make-vfs output))
2024-10-03 23:57:22 +00:00
2024-10-04 01:21:07 +00:00
;; List extracted from go src/go/build/build.go.
2024-10-03 23:57:22 +00:00
(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)))))
;; Returns a subset of `vfs`, filtered down to only contain the contents of files that
;; Go cares about.
(define (vfs-filter-for-go-package vfs)
(vfs-dir-filter vfs
(lambda (dir fname contents)
2024-10-03 23:57:22 +00:00
(define extension (extract-extension fname (- (string-length fname) 1)))
(member extension good-extensions))))
;; Generates a representation of the `vfs`, processed to be turned into JSON.
;; Primarily used by `(zilch lang go package)` to allow extracting the necessary
;; information of each package from a module's vfs.
(define (vfs-to-json vfs)
(mapping-map->list
(lambda (k v) (list (car k) (cdr k) (if (eq? v 'directory) "" v)))
(vfs-contents vfs)))))