;; Higher-level utilities to write Go compilation instructions inside of Zilch. ;; These act on a `` record, which is generated by `go-package-compile`, ;; and the final program linked together by `go-package-link`. (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 (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 ( pkg out) (fprintf out "#" (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 ``, prepending to the `vals` list, ;; and returning the resulting 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) ;; Build a Zilch-defined Go package of one store path as source code, and a list of dependencies. ;; ;; - `name` is the full name of the package, or `main` if the package is the main package. ;; - `path` is the full name of the package (e.g. `example.com/foo/bar`). ;; - `deps` is a list of `` dependencies. ;; - `source-files` is a (zexp) alist of file name to their location on disk (or store path). ;; - `assembly-files` is identical to `source-files`, but for `.s` files. ;; - `assembly-includes` is either a single on-disk path pointing to a directory, or an alist of file name to on-disk location for header files that should be in scope for `#include` in assembly files. ;; - `embed-patterns` is an alist of Go embed patterns to the filenames they contain; ;; - `embed-filenames` is an alist of filenames used in embed patterns to their on-disk location. (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)) ; 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)))) (make-go-package name path (cdr (assoc "api" compiled-go)) merged-code deps)))) ;; Link a `` into a binary that can be 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"))))))