Go: proposal: cmd/go: mendukung penyematan aset statis (file) dalam binari

Dibuat pada 4 Des 2019  ·  176Komentar  ·  Sumber: golang/go

Ada banyak alat untuk menyematkan file aset statis ke dalam binari:

Sebenarnya, https://tech.townsourced.com/post/embedding-static-files-in-go/ mencantumkan lebih banyak:

Usul

Saya pikir inilah saatnya untuk melakukan ini dengan baik sekali & mengurangi duplikasi, menambahkan dukungan resmi untuk menyematkan sumber daya file ke dalam alat cmd/go.

Masalah dengan situasi saat ini:

  • Ada terlalu banyak alat
  • Menggunakan solusi berbasis go:generate mengasapi riwayat git dengan salinan kedua (dan sedikit lebih besar) dari setiap file.
  • Tidak menggunakan go:generate berarti tidak dapat go install -atau membuat orang menulis Makefile mereka sendiri, dll.

Sasaran:

  • jangan periksa file yang dihasilkan
  • jangan buat file *.go sama sekali (setidaknya tidak di ruang kerja pengguna)
  • buat go install / go build melakukan penyematan secara otomatis
  • biarkan pengguna memilih per file/glob jenis akses mana yang diperlukan (misalnya []byte, func() io.Reader , io.ReaderAt , dll)
  • Mungkin menyimpan aset yang dikompresi dalam biner jika sesuai (misalnya jika pengguna hanya membutuhkan io.Reader )? ( edit : tapi mungkin tidak; lihat komentar di bawah)
  • Tidak ada eksekusi kode pada waktu kompilasi ; itu adalah kebijakan Go yang sudah lama ada. go build atau go install tidak dapat menjalankan kode arbitrer, seperti halnya go:generate tidak berjalan secara otomatis pada saat instalasi.

Dua pendekatan implementasi utama adalah //go:embed Logo logo.jpg atau paket terkenal ( var Logo = embed.File("logo.jpg") ).

pergi:sematkan pendekatan

Untuk pendekatan go:embed , dapat dikatakan bahwa file go/build -selected *.go dapat berisi sesuatu seperti:

//go:embed Logo logo.jpg

Yang, katakanlah, dikompilasi menjadi:

func Logo() *io.SectionReader

(menambahkan ketergantungan ke paket io )

Atau:

//go:embedglob Assets assets/*.css assets/*.js

kompilasi ke, katakan:

var Assets interface{
     Files() []string
     Open func(name string) *io.SectionReader
} = runtime.EmbedAsset(123)

Jelas ini tidak sepenuhnya disempurnakan. Perlu ada sesuatu untuk file terkompresi juga yang hanya menghasilkan io.Reader .

pendekatan paket embed

Pendekatan tingkat tinggi lainnya adalah tidak memiliki sintaks //go:embed ajaib dan sebaliknya membiarkan pengguna menulis kode Go di beberapa paket "embed" atau "golang.org/x/foo/embed" :

var Static = embed.Dir("static")
var Logo = embed.File("images/logo.jpg")
var Words = embed.CompressedReader("dict/words")

Kemudian minta cmd/go mengenali panggilan ke embed.Foo("foo/*.js") dll dan glob melakukan pekerjaan di cmd/go, daripada saat runtime. Atau mungkin tag atau flag build tertentu dapat membuatnya kembali melakukan sesuatu saat runtime. Perkeep (ditautkan di atas) memiliki mode seperti itu, yang bagus untuk mempercepat pengembangan tambahan di mana Anda tidak peduli untuk menautkan satu biner besar.

Kekhawatiran

  • Pilih gaya (//go:embed* vs paket ajaib).
  • Blokir file tertentu?

    • Mungkin memblokir penyematan ../../../../../../../../../../etc/shadow

    • Mungkin memblokir mencapai .git juga

Proposal Proposal-Hold

Komentar yang paling membantu

@robpike dan saya membicarakan proposal untuk melakukan ini bertahun-tahun yang lalu (sebelum ada proses proposal) dan tidak pernah kembali melakukan apa pun. Sudah mengganggu saya selama bertahun-tahun bahwa kami tidak pernah selesai melakukan itu. Ide yang saya ingat adalah hanya memiliki nama direktori khusus seperti "statis" yang berisi data statis dan secara otomatis membuatnya tersedia melalui API, tanpa perlu anotasi.

Saya tidak yakin tentang kerumitan tombol "terkompresi vs tidak". Jika kita melakukan itu, maka orang akan ingin kita menambahkan kontrol atas kompresi mana, tingkat kompresi, dan sebagainya. Yang perlu kita tambahkan hanyalah kemampuan untuk menyematkan file byte biasa. Jika pengguna ingin menyimpan data terkompresi dalam file itu, bagus, detailnya terserah mereka dan tidak diperlukan API sama sekali di sisi Go.

Semua 176 komentar

Perlu dipertimbangkan apakah embedglob harus mendukung pohon file lengkap, mungkin menggunakan sintaks ** didukung oleh beberapa shell Unix.

Beberapa orang akan membutuhkan kemampuan untuk menyajikan aset yang disematkan dengan HTTP menggunakan http.FileServer .

Saya pribadi menggunakan mjibson/esc (yang melakukan itu) atau dalam beberapa kasus implementasi penyematan file saya sendiri yang mengganti nama file untuk membuat jalur unik dan menambahkan peta dari jalur asli ke yang baru, misalnya "/js/bootstrap.min.js": "/js/bootstrap.min.827ccb0eea8a706c4c34a16891f84e7b.js" . Kemudian Anda dapat menggunakan peta ini dalam templat seperti ini: href="{{ static_path "/css/bootstrap.min.css" }}" .

Saya pikir konsekuensi dari ini adalah bahwa tidak mudah untuk mencari tahu file apa yang diperlukan untuk membangun sebuah program.

Pendekatan //go:embed memperkenalkan tingkat kerumitan lain. Anda harus mengurai komentar ajaib untuk bahkan mengetikkan kode. Pendekatan "paket embed" tampaknya lebih ramah untuk analisis statis.

(Hanya merenung dengan keras di sini.)

@opennota ,

akan membutuhkan kemampuan untuk melayani aset yang disematkan dengan HTTP menggunakan http.FileServer .

Ya, link pertama di atas adalah paket yang saya tulis ( tahun 2011, sebelum Go 1 ) dan masih digunakan, dan mendukung menggunakan http.FileServer: https://godoc.org/perkeep.org/pkg/fileembed#Files.Open

@cepare ,

Pendekatan //go:embed juga memperkenalkan tingkat kerumitan lain. Anda harus mengurai komentar ajaib untuk bahkan mengetikkan kode. Pendekatan "paket embed" tampaknya lebih ramah untuk analisis statis.

Ya, poin yang bagus. Itu argumen yang sangat kuat untuk menggunakan sebuah paket. Itu juga membuatnya lebih mudah dibaca & didokumentasikan, karena kita dapat mendokumentasikan semuanya dengan godoc biasa, daripada jauh di dalam dokumen cmd/go.

@bradfitz - Apakah Anda ingin menutup ini https://github.com/golang/go/issues/3035 ?

@agnivade , terima kasih telah menemukannya! Saya pikir saya ingat itu tetapi tidak dapat menemukannya. Mari kita biarkan terbuka untuk saat ini dan lihat apa yang orang lain pikirkan.

Jika kita menggunakan paket ajaib, kita bisa menggunakan trik tipe yang tidak diekspor untuk memastikan bahwa pemanggil melewatkan konstanta waktu kompilasi sebagai argumen: https://play.golang.org/p/RtHlKjhXcda.

(Ini adalah strategi yang dirujuk di sini: https://groups.google.com/forum/#!topic/golang-nuts/RDA9Hag8RZw/discussion)

Satu kekhawatiran yang saya miliki adalah bagaimana menangani invividual atau semua aset menjadi terlalu besar untuk masuk ke dalam memori dan apakah mungkin ada tag build atau opsi akses per file untuk memilih antara memprioritaskan waktu akses vs jejak memori atau beberapa implementasi jalan tengah.

cara saya memecahkan masalah itu (karena tentu saja saya juga memiliki implementasi saya sendiri :)) adalah dengan menyediakan implementasi http.FileSystem yang melayani semua aset yang disematkan. Dengan begitu, Anda tidak bergantung pada komentar ajaib untuk menenangkan pemeriksa tipe, aset dapat dengan mudah dilayani oleh http, implementasi fallback dapat disediakan untuk tujuan pengembangan (http.Dir) tanpa mengubah kode, dan yang terakhir implementasinya cukup fleksibel, karena http.FileSystem mencakup sedikit, tidak hanya dalam membaca file, tetapi juga membuat daftar direktori.

Seseorang masih dapat menggunakan komentar ajaib atau apa pun untuk menentukan apa yang perlu disematkan, meskipun mungkin lebih mudah untuk menentukan semua gumpalan melalui file teks biasa.

@AlexRouSg Proposal ini hanya untuk file yang sesuai untuk disertakan secara langsung dalam executable akhir. Tidak tepat menggunakan ini untuk file yang terlalu besar untuk muat di memori. Tidak ada alasan untuk mempersulit alat ini untuk menangani kasus itu; untuk itu, jangan gunakan alat ini.

@ianlancetaylor , saya pikir perbedaan yang dibuat @AlexRouSg adalah antara menyediakan file sebagai global []byte s (tidak dapat dihalaman, memori yang berpotensi dapat ditulis) vs menyediakan tampilan hanya-baca, sesuai permintaan dari bagian ELF yang dapat biasanya hidup di disk (dalam executable), seperti melalui panggilan Open yang mengembalikan *io.SectionReader . (Saya tidak ingin memasukkan http.File atau http.FileSystem ke dalam cmd/go atau runtime... net/http dapat menyediakan adaptor.)

@bradfitz keduanya http.File itu sendiri adalah antarmuka tanpa ketergantungan teknis ke paket http . Mungkin ide yang bagus untuk metode Open untuk menyediakan implementasi yang sesuai dengan antarmuka tersebut, karena metode Stat dan Readdir cukup berguna untuk aset tersebut

@urandom , itu tidak bisa mengimplementasikan http.FileSystem, tanpa mengacu pada nama "http.File" (https://play.golang.org/p/-r3KjG1Gp-8).

@robpike dan saya membicarakan proposal untuk melakukan ini bertahun-tahun yang lalu (sebelum ada proses proposal) dan tidak pernah kembali melakukan apa pun. Sudah mengganggu saya selama bertahun-tahun bahwa kami tidak pernah selesai melakukan itu. Ide yang saya ingat adalah hanya memiliki nama direktori khusus seperti "statis" yang berisi data statis dan secara otomatis membuatnya tersedia melalui API, tanpa perlu anotasi.

Saya tidak yakin tentang kerumitan tombol "terkompresi vs tidak". Jika kita melakukan itu, maka orang akan ingin kita menambahkan kontrol atas kompresi mana, tingkat kompresi, dan sebagainya. Yang perlu kita tambahkan hanyalah kemampuan untuk menyematkan file byte biasa. Jika pengguna ingin menyimpan data terkompresi dalam file itu, bagus, detailnya terserah mereka dan tidak diperlukan API sama sekali di sisi Go.

Beberapa pemikiran:

  • Seharusnya tidak mungkin untuk menyematkan file apa pun di luar modul yang melakukan penyematan. Kita perlu memastikan file adalah bagian dari file zip modul saat kita membuatnya, jadi itu juga berarti tidak ada tautan simbolik, konflik huruf besar-kecil, dll. Kita tidak dapat mengubah algoritme yang menghasilkan file zip tanpa melanggar jumlah.
  • Saya pikir lebih mudah untuk membatasi penyematan berada di direktori yang sama (jika //go:embed komentar digunakan) atau subdirektori tertentu (jika static digunakan). Ini membuatnya lebih mudah untuk memahami hubungan antara paket dan file yang disematkan.

Either way, ini memblokir penyematan /etc/shadow atau .git . Keduanya tidak dapat dimasukkan dalam zip modul.

Secara umum, saya khawatir tentang memperluas cakupan perintah go terlalu banyak. Namun, fakta bahwa ada begitu banyak solusi untuk masalah ini berarti mungkin harus ada satu solusi resmi.

Saya akrab dengan go_embed_data dan go-bindata (di antaranya ada beberapa garpu), dan ini tampaknya mencakup kasus penggunaan tersebut. Apakah ada masalah penting yang dipecahkan orang lain yang tidak tercakup?

Memblokir file tertentu seharusnya tidak terlalu sulit, terutama jika Anda menggunakan direktori static atau embed . Symlinks mungkin sedikit memperumitnya, tetapi Anda dapat mencegahnya menyematkan apa pun di luar modul saat ini atau, jika Anda menggunakan GOPATH, di luar paket yang berisi direktori.

Saya bukan penggemar komentar yang dikompilasi ke kode, tetapi saya juga menemukan paket semu yang memengaruhi kompilasi juga agak aneh. Jika pendekatan direktori tidak digunakan, mungkin lebih masuk akal untuk memiliki semacam deklarasi tingkat atas embed -benar dibangun ke dalam bahasa. Ini akan bekerja mirip dengan import , tetapi hanya akan mendukung jalur lokal dan memerlukan nama untuk ditugaskan. Sebagai contoh,

embed ui "./ui/build"

func main() {
  file, err := ui.Open("version.txt")
  if err != nil {
    panic(err)
  }
  version, err = ioutil.ReadAll(file)
  if err != nil {
    panic(err)
  }
  file.Close()

  log.Printf("UI Version: %s\n", bytes.TrimSpace(version))
  http.ListenAndServe(":8080", http.EmbeddedDir(ui))
}

Sunting: Anda mengalahkan saya, @jayconrod.

Untuk memperluas https://github.com/golang/go/issues/35950#issuecomment -561703346 , ada teka-teki tentang API yang terbuka. Cara yang jelas untuk mengekspos data adalah []byte , string , dan Read -ish interfaces.

Kasus tipikal adalah Anda ingin data yang disematkan tidak dapat diubah. Namun, semua antarmuka yang mengekspos []byte (termasuk io.Reader , io.SectionReader , dll.) harus (1) membuat salinan, (2) memungkinkan perubahan, atau (3) menjadi abadi meskipun []byte . Mengekspos data sebagai string s menyelesaikannya, tetapi dengan mengorbankan API yang seringkali berakhir dengan membutuhkan penyalinan, karena banyak kode yang menggunakan file yang disematkan pada akhirnya membutuhkan irisan byte dengan satu atau lain cara.

Saya menyarankan route (3): tidak dapat diubah meskipun []byte . Anda dapat menerapkan ini dengan murah dengan menggunakan simbol readonly untuk backing array. Ini juga memungkinkan Anda mengekspos data yang sama dengan aman sebagai []byte dan string ; upaya untuk mengubah data akan gagal. Kompiler tidak dapat memanfaatkan kekekalan, tetapi itu bukan kerugian yang terlalu besar. Ini adalah sesuatu yang dapat dibawa oleh dukungan toolchain ke tabel yang (sejauh yang saya tahu) tidak ada paket codegen yang ada.

(Paket codegen pihak ketiga dapat melakukan ini dengan membuat file assembly generik yang berisi simbol DATA yang ditandai sebagai readonly, dan kemudian file assembly arch-spesifik yang memperlihatkan simbol-simbol tersebut dalam bentuk string s dan []byte s. Saya menulis CL 163747 secara khusus dengan

Saya tidak yakin apa yang Anda bicarakan dalam hal kekekalan. io.Reader sudah memberlakukan kekekalan. Itulah intinya. Saat Anda memanggil Read(buf) , itu menyalin data ke buffer yang disediakan _you_. Mengubah buf setelah itu tidak memiliki efek pada internal io.Reader .

Saya setuju dengan @DeedleFake. Saya tidak ingin bermain game dengan dukungan array []byte ajaib. Tidak apa-apa untuk menyalin dari biner ke buffer yang disediakan pengguna.

Hanya kerutan lain di sini - Saya memiliki proyek berbeda yang menggunakan kode sumber DTrace (tertanam). Ini sensitif terhadap perbedaan antara \n dan \r\n. (Kita dapat memperdebatkan apakah ini hal yang bodoh di DTrace atau tidak -- itu tidak penting dan itulah situasinya saat ini.)

Sangat berguna bahwa string backticked memperlakukan keduanya sebagai \n terlepas dari bagaimana mereka muncul di sumber, dan saya mengandalkan ini dengan go-generate untuk menyematkan DTrace.

Jadi jika ada file embed yang ditambahkan ke perintah go, saya akan dengan lembut menyarankan bahwa opsi untuk mengubah penanganan CR/CRLF mungkin sangat berguna, terutama untuk orang-orang yang mungkin mengembangkan pada sistem yang berbeda di mana akhiran baris default dapat sebuah gotcha.

Seperti halnya kompresi, saya benar-benar ingin berhenti di "menyalin byte file ke dalam biner". Normalisasi CR/CRLF, normalisasi Unicode, gofmt'ing, semua milik tempat lain. Periksa file yang berisi byte persis yang Anda inginkan. (Jika kontrol versi Anda tidak dapat membiarkannya sendiri, mungkin periksa konten yang di-gzip dan gunzip pada saat runtime.) Ada _banyak_ tombol munging file yang dapat kita bayangkan untuk ditambahkan. Mari kita berhenti di 0.

Mungkin sudah terlambat untuk memperkenalkan nama direktori baru yang dipesan, sebanyak yang saya mau.
(Tidak terlalu terlambat di tahun 2014, tapi mungkin sudah terlambat sekarang.)
Jadi semacam komentar keikutsertaan mungkin diperlukan.

Misalkan kita mendefinisikan tipe runtime.Files. Kemudian Anda bisa membayangkan menulis:

//go:embed *.html (or static/* etc)
var files runtime.Files

Dan kemudian saat runtime Anda cukup memanggil files.Open untuk mendapatkan kembali interface { io.ReadSeeker; io.ReaderAt } dengan datanya. Perhatikan bahwa var tidak diekspor, jadi satu paket tidak dapat mengobrak-abrik file tertanam paket lain.

Nama TBD tetapi sejauh mekanisme sepertinya sudah cukup, dan saya tidak melihat bagaimana membuatnya lebih sederhana. (Tentu saja penyederhanaan diterima!)

Apa pun yang kita lakukan, itu harus memungkinkan untuk mendukung dengan Bazel dan Gazelle juga. Itu berarti meminta Gazelle mengenali komentar dan menulis aturan Bazel yang mengatakan gumpalan, dan kemudian kita perlu mengekspos alat (go tool embedgen atau apa pun) untuk menghasilkan file tambahan untuk disertakan dalam build (perintah go akan lakukan ini secara otomatis dan tidak pernah benar-benar menampilkan file tambahan). Itu tampaknya cukup mudah.

Jika berbagai munging tidak berhasil, maka itu adalah argumen untuk tidak menggunakan fasilitas baru ini. Ini bukan penghalang bagi saya -- saya dapat menggunakan go generate seperti yang telah saya lakukan, tetapi itu berarti saya tidak dapat mengambil manfaat dari fitur baru tersebut.

Sehubungan dengan munging secara umum -- saya dapat membayangkan solusi di mana seseorang menyediakan implementasi antarmuka (sesuatu seperti Reader() di satu sisi, dan sesuatu untuk menerima file di sisi lain -- mungkin dibuat dengan io.Reader dari file itu sendiri) -- yang akan dibuat dan dijalankan oleh go cmd untuk memfilter file sebelum disematkan. Kemudian orang dapat memberikan filter apa pun yang mereka inginkan. Saya membayangkan beberapa orang akan menyediakan filter kuasi-standar seperti implementasi dos2unix, kompresi, dll. (Mungkin mereka bahkan dapat dirantai.)

Saya kira harus ada asumsi bahwa apa pun prosesor yang disematkan, itu harus dapat dikompilasi pada ~ setiap sistem build, karena go akan membangun alat asli sementara untuk tujuan ini.

Mungkin sudah terlambat untuk memperkenalkan nama direktori baru yang dipesan, sebanyak yang saya mau. [...] semacam komentar keikutsertaan mungkin diperlukan.

Jika file hanya dapat diakses melalui paket khusus, misalnya runtime/embed , maka mengimpor paket tersebut dapat menjadi sinyal keikutsertaan.

Pendekatan io.Read sepertinya dapat menambah overhead yang signifikan (dalam hal penyalinan dan jejak memori) untuk operasi linier yang sederhana secara konseptual seperti strings.Contains (seperti dalam cmd/go/internal/cfg ) atau , kritis, template.Parse .

Untuk kasus penggunaan tersebut, tampaknya ideal untuk mengizinkan penelepon memilih apakah akan memperlakukan seluruh gumpalan sebagai (mungkin dipetakan dengan memori) string atau io.ReaderAt .

Itu tampaknya kompatibel dengan pendekatan umum runtime.Files : hal yang dikembalikan dari runtime.Files.Open dapat memiliki metode ReadString() string yang mengembalikan representasi yang dipetakan memori.

semacam komentar keikutsertaan mungkin diperlukan.

Kita bisa melakukannya dengan versi go dalam file go.mod . Sebelum 1.15 (atau apa pun) subdirektori static akan berisi sebuah paket, dan pada 1.15 atau lebih tinggi akan berisi aset yang disematkan.

(Namun, itu tidak terlalu membantu dalam mode GOPATH .)

Saya tidak yakin tentang kerumitan tombol "terkompresi vs tidak". Jika kita melakukan itu, maka orang akan ingin kita menambahkan kontrol atas kompresi mana, tingkat kompresi, dan sebagainya. Yang perlu kita tambahkan hanyalah kemampuan untuk menyematkan file byte biasa.

Meskipun saya menghargai dorongan untuk kesederhanaan, kami juga harus memastikan bahwa kami memenuhi kebutuhan pengguna.

12 dari 14 alat yang terdaftar di https://tech.townsourced.com/post/embedding-static-files-in-go/#comparison mendukung kompresi, yang menunjukkan bahwa itu adalah persyaratan yang cukup umum.

Memang benar bahwa seseorang dapat melakukan kompresi sebagai langkah pra-pembuatan di luar go, tetapi itu masih memerlukan 1) alat untuk melakukan kompresi 2) memeriksa semacam assets.zip gumpalan ke dalam vcs 3) mungkin sebuah utilitas perpustakaan di sekitar api embed untuk membatalkan kompresi. Pada titik mana tidak jelas apa manfaatnya sama sekali.

Tiga dari tujuan yang tercantum dalam proposal awal adalah:

  • jangan periksa file yang dihasilkan
  • make go install / go build lakukan penyematan secara otomatis
  • simpan aset yang dikompresi dalam biner jika sesuai

Jika kita membaca yang kedua sebagai "tidak memerlukan alat terpisah untuk menyematkan" maka tidak mendukung file terkompresi secara langsung atau tidak langsung gagal memenuhi ketiga tujuan ini.

Apakah ini perlu level paket? Level modul tampaknya merupakan perincian yang lebih baik karena kemungkinan besar satu modul = satu proyek.

Karena direktori ini tidak akan berisi kode Go, mungkinkah itu seperti _static ?

atau, jika ya, akan diperlakukan sebagai byte arbitrer yang namanya berakhiran ".go" alih-alih sebagai kode Go yang akan dikompilasi

Jika itu satu direktori khusus, logikanya bisa saja menyeruput apa saja dan semua yang ada di pohon direktori itu. Paket penyematan ajaib memungkinkan Anda melakukan sesuatu seperti embed.Open("img/logo.svg") untuk membuka file di subdirektori pohon aset.

String tampaknya cukup baik. Mereka dapat dengan mudah disalin ke []byte atau diubah menjadi Reader . Pembuatan kode atau pustaka dapat digunakan untuk menyediakan API yang lebih menarik dan menangani berbagai hal selama init . Itu bisa termasuk dekompresi atau membuat http.FileSystem .

Bukankah Windows memiliki format khusus untuk menyematkan aset. Haruskah itu digunakan saat membangun Windows yang dapat dieksekusi? Jika demikian, apakah itu memiliki implikasi untuk jenis operasi yang dapat disediakan?

Jangan lupa gitfs 😂

Apakah ada alasan mengapa itu tidak bisa menjadi bagian dari go build / link... misalnya go build -embed example=./path/example.txt dan beberapa paket yang memperlihatkan akses ke sana (misalnya embed.File("example") , alih-alih menggunakan go:embed ?

Anda memerlukan rintisan untuk itu dalam kode Anda

@egonelbre masalah dengan go build -embed adalah bahwa semua pengguna harus menggunakannya dengan benar. Ini harus sepenuhnya transparan dan otomatis; perintah go install atau go get tidak dapat berhenti melakukan hal yang benar.

@bradfitz saya akan merekomendasikan https://github.com/markbates/pkger melalui Packr. Ini menggunakan API perpustakaan standar untuk bekerja dengan file.

func run() error {
    f, err := pkger.Open("/public/index.html")
    if err != nil {
        return err
    }
    defer f.Close()

    info, err := f.Stat()
    if err != nil {
        return err
    }

    fmt.Println("Name: ", info.Name())
    fmt.Println("Size: ", info.Size())
    fmt.Println("Mode: ", info.Mode())
    fmt.Println("ModTime: ", info.ModTime())

    if _, err := io.Copy(os.Stdout, f); err != nil {
        return err
    }
    return nil
}

Atau mungkin tag atau flag build tertentu dapat membuatnya kembali melakukan sesuatu saat runtime. Perkeep (ditautkan di atas) memiliki mode seperti itu, yang bagus untuk mempercepat pengembangan tambahan di mana Anda tidak peduli untuk menautkan satu biner besar.

mjibson/esc melakukan ini juga, dan ini merupakan peningkatan kualitas hidup yang besar saat mengembangkan aplikasi web; Anda tidak hanya menghemat waktu penautan tetapi juga menghindari keharusan memulai ulang aplikasi, yang dapat memakan waktu lama dan/atau memerlukan pengulangan langkah ekstra untuk menguji perubahan Anda, tergantung pada implementasi aplikasi web.

Masalah dengan situasi saat ini:

  • Menggunakan solusi berbasis go:generate mengasapi riwayat git dengan salinan kedua (dan sedikit lebih besar) dari setiap file.

Sasaran:

  • jangan periksa file yang dihasilkan

Nah, bagian ini mudah dipecahkan hanya dengan menambahkan file yang dihasilkan ke file .gitignore atau yang setara. aku selalu melakukan itu...

Jadi, Go bisa saja memiliki alat sematan "resmi" sendiri yang berjalan secara default pada go build dan meminta orang untuk mengabaikan file-file ini sebagai sebuah konvensi. Itu akan menjadi solusi ajaib yang tersedia (dan kompatibel dengan versi Go yang ada).

Saya hanya brainstorming / berpikir keras di sini ... tapi saya sebenarnya menyukai ide yang diajukan secara keseluruhan. 🙂

Juga, karena direktif //go:generate tidak berjalan secara otomatis pada go build , perilaku go build mungkin tampak sedikit tidak konsisten: //go:embed akan bekerja secara otomatis tetapi untuk //go:generate Anda harus menjalankan go generate secara manual. ( //go:generate sudah dapat memutus aliran go get jika menghasilkan .go file yang diperlukan untuk pembangunan).

//go:generate sudah dapat menghentikan aliran get go jika menghasilkan .go file yang diperlukan untuk build

Saya pikir alur yang biasa untuk itu, dan yang biasanya saya gunakan, meskipun perlu sedikit membiasakan diri, adalah menggunakan go generate seluruhnya sebagai alat pengembangan-akhir dan hanya mengkomit file yang itu menghasilkan.

@bradfitz itu tidak perlu mengimplementasikan http.FileSystem itu sendiri. Jika implementasi menyediakan tipe yang mengimplementasikan http.File , maka itu akan sepele bagi siapa saja, termasuk paket http stdlib untuk menyediakan pembungkus di sekitar fungsi Open , mengonversi tipe menjadi http.File agar sesuai dengan http.FileSystem

@andreynering //go:generate dan //go:embed sangat berbeda. Mekanisme ini dapat terjadi dengan mulus pada waktu pembuatan karena tidak akan menjalankan kode arbitrer. Saya percaya itu membuatnya mirip dengan bagaimana cgo dapat menghasilkan kode sebagai bagian dari go build .

Saya tidak yakin tentang kerumitan tombol "terkompresi vs tidak". Jika kita melakukan itu, maka orang akan ingin kita menambahkan kontrol atas kompresi mana, tingkat kompresi, dan sebagainya. Yang perlu kita tambahkan hanyalah kemampuan untuk menyematkan file byte biasa.

Meskipun saya menghargai dorongan untuk kesederhanaan, kami juga harus memastikan bahwa kami memenuhi kebutuhan pengguna.

12 dari 14 alat yang terdaftar di https://tech.townsourced.com/post/embedding-static-files-in-go/#comparison mendukung kompresi, yang menunjukkan bahwa itu adalah persyaratan yang cukup umum.

Saya tidak yakin saya setuju dengan alasan ini.

Kompresi yang dilakukan oleh perpustakaan lain berbeda dengan menambahkannya ke proposal ini karena mereka tidak akan mengurangi kinerja pada build berikutnya karena alternatifnya secara umum dihasilkan sebelum build daripada selama waktu build.

Waktu pembuatan yang rendah adalah nilai tambah yang jelas dengan Go over bahasa lain dan kompresi memperdagangkan waktu CPU untuk mengurangi jejak penyimpanan/transfer. Jika banyak paket Go mulai menjalankan kompresi pada go build kita akan menambahkan lebih banyak waktu pembuatan daripada waktu yang ditambahkan hanya dengan menyalin aset selama pembuatan. Saya skeptis untuk menambahkan kompresi karena orang lain melakukannya. Selama desain awal tidak dengan desain mencegah ekstensi masa depan yang menambahkan dukungan untuk yaitu kompresi, meletakkannya di sana karena mungkin sesuatu yang dapat menguntungkan beberapa tampaknya seperti lindung nilai yang tidak perlu.

Ini tidak seperti penyematan file tidak akan berguna tanpa kompresi, kompresi adalah hal yang baik untuk mengurangi ukuran biner dari mungkin 100MB menjadi 50MB — yang bagus, tetapi juga bukan pemecah masalah yang jelas untuk fungsionalitas untuk sebagian besar aplikasi yang dapat saya pikirkan . Apalagi jika sebagian besar aset "lebih berat" adalah file seperti JPEG atau PNG yang sudah dikompresi dengan cukup baik.

Bagaimana dengan menjaga kompresi keluar untuk saat ini dan menambahkannya jika itu benar-benar dilewatkan oleh banyak orang? (dan dapat dilakukan tanpa biaya yang tidak semestinya)

Untuk menambah komentar @sakjur di atas: kompresi tampaknya ortogonal bagi saya. Saya biasanya ingin mengompres seluruh biner atau melepaskan arsip, dan bukan hanya aset. Terutama ketika binari Go di Go dapat dengan mudah mencapai puluhan megabita tanpa aset apa pun.

@mvdan Saya kira salah satu kekhawatiran saya adalah bahwa cukup sering ketika saya melihat embedding bersama dengan beberapa pra-pemrosesan lainnya: minifikasi, kompilasi TypeScript, kompresi data, pemecahan gambar, pengubahan ukuran gambar, sprite-sheet. Satu-satunya pengecualian adalah situs web yang hanya menggunakan html/template . Jadi, pada akhirnya, Anda mungkin akan menggunakan semacam "Makefile" atau mengunggah konten yang telah diproses sebelumnya. Dalam hal itu, saya akan berpikir bahwa flag baris perintah akan bekerja lebih baik dengan alat lain daripada komentar.

Saya kira salah satu kekhawatiran saya adalah bahwa cukup sering ketika saya melihat embedding bersama dengan beberapa pra-pemrosesan lainnya: minifikasi, kompilasi TypeScript, kompresi data, pemecahan gambar, pengubahan ukuran gambar, sprite-sheet. Satu-satunya pengecualian adalah situs web yang hanya menggunakan html/templat.

Terima kasih, itu titik data yang berguna. Mungkin kebutuhan akan kompresi tidak seumum kelihatannya. Jika itu masalahnya, saya setuju bahwa masuk akal untuk meninggalkannya.

Ini tidak seperti penyematan file tidak akan berguna tanpa kompresi, kompresi adalah hal yang baik untuk mengurangi ukuran biner dari mungkin 100MB menjadi 50MB — yang bagus, tetapi juga bukan pemecah masalah yang jelas untuk fungsionalitas untuk sebagian besar aplikasi yang dapat saya pikirkan .

Ukuran biner adalah masalah besar bagi banyak pengembang go (https://github.com/golang/go/issues/6853). Go kompres info debug DWARF secara khusus untuk mengurangi ukuran biner, meskipun ini memerlukan biaya untuk menghubungkan waktu (https://github.com/golang/go/issues/11799, https://github.com/golang/go/ masalah/26074). Jika ada cara mudah untuk memotong ukuran biner menjadi dua, saya pikir para pengembang akan melompat pada kesempatan itu (walaupun saya ragu keuntungan di sini akan hampir signifikan).

Itu tidak terlalu membantu dalam mode GOPATH

Mungkin, jika Anda dalam mode GOPATH, fitur ini tidak berlaku karena saya membayangkan tim Go tidak berencana melakukan paritas fitur untuk GOPATH selamanya? Sudah ada fitur yang tidak didukung di GOPATH (seperti keamanan dengan checksum db, mengunduh dependensi melalui server proxy, dan versi impor semantik)

Seperti yang disebutkan @bcmills , memiliki nama direktori statis dalam file go.mod adalah cara yang bagus untuk memperkenalkan fitur ini di Go 1.15 karena fitur tersebut dapat dimatikan secara otomatis di file go.mod yang memiliki klausa <=go1.14.

Yang mengatakan, ini juga berarti pengguna harus menulis secara manual apa jalur direktori statis itu.

Saya pikir direktori vendor dan konvensi _test.go adalah contoh bagus tentang bagaimana mereka membuat bekerja dengan Go dan kedua fitur itu jauh lebih mudah.

Saya tidak ingat banyak orang meminta opsi untuk menyesuaikan nama direktori vendor atau memiliki kemampuan untuk mengubah konvensi _test.go menjadi sesuatu yang lain. Tetapi jika Go tidak pernah memperkenalkan fitur _test.go, maka pengujian di Go akan terlihat jauh berbeda hari ini.

Oleh karena itu, mungkin nama yang kurang umum dari static memberikan peluang non-tabrakan yang lebih baik sehingga memiliki direktori konvensional (mirip dengan vendor dan _test.go) dapat menjadi pengalaman pengguna yang lebih baik dibandingkan dengan komentar ajaib.

Contoh nama yang berpotensi tabrakan rendah:

  • _embed - mengikuti konvensi _test.go
  • go_binary_assets
  • .gobin mengikuti konvensi .git
  • runtime_files - sehingga cocok dengan runtime.Files struct

Terakhir, direktori vendor telah ditambahkan di Go 1.5 . Sooo, mungkin tidak terlalu buruk untuk menambahkan konvensi baru sekarang? 😅

Saya pikir itu harus mengekspos mmap-readonly []byte . Hanya akses mentah ke halaman dari yang dapat dieksekusi, di-page oleh OS sesuai kebutuhan. Segala sesuatu yang lain dapat disediakan di atas itu, hanya dengan bytes.NewReader .

Jika ini karena alasan tertentu tidak dapat diterima, harap berikan ReaderAt bukan hanya ReadSeeker ; yang terakhir sepele untuk dibangun dari yang pertama, tetapi cara lain tidak sebaik: itu akan membutuhkan mutex untuk menjaga offset tunggal, dan merusak kinerja.

Ini tidak seperti penyematan file tidak akan berguna tanpa kompresi, kompresi adalah hal yang baik untuk mengurangi ukuran biner dari mungkin 100MB menjadi 50MB — yang bagus, tetapi juga bukan pemecah masalah yang jelas untuk fungsionalitas untuk sebagian besar aplikasi yang dapat saya pikirkan .

Ukuran biner adalah masalah besar bagi banyak pengembang go (#6853). Go kompres info debug DWARF secara khusus untuk mengurangi ukuran biner, meskipun ini memerlukan biaya untuk menghubungkan waktu (#11799, #26074). Jika ada cara mudah untuk memotong ukuran biner menjadi dua, saya pikir para pengembang akan melompat pada kesempatan itu (walaupun saya ragu keuntungan di sini akan hampir signifikan).

Itu jelas merupakan poin yang adil dan saya dapat melihat bagaimana argumen saya dapat dilihat sebagai argumen yang mendukung kecerobohan sehubungan dengan ukuran file. Itu bukan niat saya. Maksud saya lebih sesuai dengan pengiriman fitur ini tanpa kompresi yang masih berguna bagi sebagian orang, dan mereka dapat memberikan umpan balik dan wawasan yang berguna tentang cara menambahkan kompresi dengan benar dengan cara yang terasa benar dalam jangka panjang. Aset mungkin membengkak sedemikian rupa sehingga info debug tidak mungkin dilakukan dan lebih mudah bagi pengembang paket yang diinstal/diimpor oleh orang lain untuk mengurangi kinerja pembangunan yang tidak perlu jika implementasinya membuatnya mudah untuk melakukannya.

Pilihan lain adalah menjadikan kompresi aset sebagai flag build dan menyerahkan kompromi antara ukuran dan waktu build ke pembangun daripada pengembang. Itu akan memindahkan keputusan lebih dekat ke pengguna akhir biner yang dapat membuat keputusan apakah kompresi itu bermanfaat. Otoh, ini akan berisiko menciptakan area permukaan yang meningkat untuk perbedaan antara pengembangan dan produksi, jadi ini bukan metode yang jelas lebih baik daripada yang lain dan itu bukan sesuatu yang saya rasa ingin saya anjurkan.

Alat penyematan aset saya saat ini memuat konten dari file aset saat dibuat dengan -tags dev . Beberapa konvensi seperti itu mungkin akan berguna di sini juga; itu memperpendek siklus pengembangan secara signifikan ketika misalnya mengutak-atik HTML atau template.

Jika tidak, pemanggil harus membungkus mekanisme tingkat yang lebih rendah ini dengan beberapa pembungkus *_dev.go dan *_nodev.go dan mengimplementasikan pemuatan yang tidak disematkan untuk skenario dev . Bahkan tidak sulit, tetapi jalan itu hanya akan menyebabkan ledakan alat serupa yang dijelaskan oleh komentar pertama tentang masalah ini. Alat-alat itu harus melakukan kurang dari hari ini, tetapi mereka masih akan berlipat ganda.

Saya pikir -tags dev gagal bekerja ketika dijalankan di luar modul Go akan masuk akal (tidak tahu dari mana memuat aset).

Bagaimana dengan hanya go tool embed yang mengambil input dan menghasilkan file keluaran Go dalam format khusus yang dikenali oleh komputer sebagai file tertanam yang kemudian dapat diakses melalui runtime/emved atau sesuatu. Maka Anda bisa melakukan //go:generate gzip -o - static.txt | go tool embed -o static.go .

Kelemahan besar, tentu saja, adalah Anda kemudian harus mengkomit file yang dihasilkan.

@DeedleFake masalah ini dimulai dengan

Menggunakan solusi berbasis go:generate mengasapi riwayat git dengan salinan kedua (dan sedikit lebih besar) dari setiap file.

Ups. Lupakan. Maaf.

Ini tidak seperti penyematan file tidak akan berguna tanpa kompresi, kompresi adalah hal yang baik untuk mengurangi ukuran biner dari mungkin 100MB menjadi 50MB — yang bagus, tetapi juga bukan pemecah masalah yang jelas untuk fungsionalitas untuk sebagian besar aplikasi yang dapat saya pikirkan .

Ukuran biner adalah masalah besar bagi banyak pengembang go (#6853). Go kompres info debug DWARF secara khusus untuk mengurangi ukuran biner, meskipun ini memerlukan biaya untuk menghubungkan waktu (#11799, #26074). Jika ada cara mudah untuk memotong ukuran biner menjadi dua, saya pikir para pengembang akan melompat pada kesempatan itu (walaupun saya ragu keuntungan di sini akan hampir signifikan).

Jika diperlukan, maka orang akan memiliki data terkompresi yang dikomit dan disematkan, dan akan ada paket untuk menyediakan lapisan antara runtime.Embed dan konsumen akhir yang melakukan dekompresi sebaris.

Dan kemudian satu atau dua tahun dari sekarang akan ada masalah baru tentang menambahkan kompresi dan dapat diurutkan kemudian.

Saya mengatakan ini sebagai salah satu dari 15 standar bersaing ketika saya menulis goembed :)

@tv42 menulis:

Saya pikir itu harus mengekspos mmap-readonly []byte . Hanya akses mentah ke halaman dari yang dapat dieksekusi, di-page oleh OS sesuai kebutuhan.

Komentar ini mudah dilewatkan dan sangat berharga.

@tv42 ,

Saya pikir itu harus mengekspos byte mmap-readonly []. Hanya akses mentah ke halaman dari yang dapat dieksekusi, di-page oleh OS sesuai kebutuhan. Segala sesuatu yang lain dapat disediakan di atas itu, hanya dengan byte.NewReader.

Jenis yang sudah read-only adalah string . Juga: ia menyediakan ukuran, tidak seperti io.ReaderAt , dan tidak bergantung pada pustaka standar. Itu mungkin yang ingin kami ungkapkan.

Jenis yang sudah read-only adalah string .

Tetapi seluruh ekosistem Write dll bekerja pada []byte . Ini pragmatisme sederhana. Saya tidak melihat bahwa properti readonly lebih menjadi masalah daripada io.Writer.Write docs mengatakan

Menulis tidak boleh mengubah data irisan, bahkan untuk sementara.

Kelemahan potensial lainnya adalah ketika menyematkan direktori dengan go:generate saya dapat memeriksa output git diff dan melihat apakah ada file yang tidak sengaja ada di sana. Dengan proposal ini - ? Mungkin perintah go akan mencetak daftar file yang disematkan?

@tv42

Tetapi seluruh ekosistem Write dll bekerja pada []byte.

html/template bekerja dengan string, meskipun.

Pergi sudah membiarkan Anda menggunakan -ldflags -X untuk mengatur beberapa string (berguna untuk mengatur versi git, waktu kompilasi, server, pengguna, dll.), dapatkah mekanisme ini diperluas untuk mengatur io.Readers alih-alih string?

@bradfitz Apakah Anda mengusulkan untuk menggunakan string di sini bahkan untuk data yang bukan teks? Cukup umum untuk menyematkan file biner kecil seperti ikon dan gambar kecil, dll.

@tv42 Anda mengatakan Write tapi saya berasumsi maksud Anda Read . Anda dapat mengubah string menjadi io.ReaderAt menggunakan strings.NewReader , jadi menggunakan string sepertinya bukan penghalang di sana.

@andreynering A string dapat menampung urutan byte apa pun.

string dapat menampung urutan byte apa pun.

Ya, tetapi tujuan utamanya adalah untuk menyimpan teks, dan bukan data yang sewenang-wenang. Saya kira ini dapat menyebabkan sedikit kebingungan, khususnya untuk pengembang Go yang tidak berpengalaman.

Aku benar-benar mendapat ide, meskipun. Terima kasih telah mengklarifikasi.

@ianlancetaylor

Read seharusnya memutasi irisan yang masuk. Write tidak. Oleh karena itu dokumentasi Write mengatakan ini tidak diperbolehkan. Saya tidak melihat ada yang lebih dibutuhkan daripada mendokumentasikan bahwa pengguna tidak boleh menulis ke []byte dikembalikan.

Hanya karena strings.Reader ada tidak berarti io.WriteString akan menemukan implementasi penulisan string yang efisien. Misalnya, TCPConn tidak memiliki WriteString .

Saya tidak suka Go menyertakan fitur baru seperti ini hanya untuk memaksa semua data disalin hanya untuk menulisnya ke soket.

Juga, asumsi umum adalah bahwa string dapat dicetak oleh manusia dan []byte seringkali tidak. Menempatkan JPEG dalam string akan menyebabkan banyak terminal yang kacau.

@opennota

html/template berfungsi dengan string.

Ya, itu aneh, hanya membutuhkan file dengan nama path bukan sebagai pembaca. Dua tanggapan:

  1. Tidak ada alasan data yang disematkan tidak dapat memiliki kedua metode Bytes() []byte dan String() string .

  2. Semoga Anda tidak mem-parsing template setiap permintaan; sedangkan Anda benar-benar harus mengirim data JPEG ke soket TCP untuk setiap permintaan yang memintanya.

@tv42 Kita bisa menambahkan metode WriteString sesuai kebutuhan.

Saya tidak berpikir penggunaan paling umum dari fungsi ini adalah untuk menulis data yang tidak dimodifikasi, jadi saya tidak berpikir kita harus mengoptimalkan untuk kasus itu.

Saya tidak berpikir penggunaan paling umum dari fungsi ini adalah untuk menulis data yang tidak dimodifikasi,

Saya pikir penggunaan paling umum dari fungsi ini adalah untuk melayani aset web, gambar/js/css, tidak dimodifikasi.

Tapi jangan mengambil kata saya untuk itu, mari kita lihat beberapa importir fileembed Brad:

#fileembed pattern .+\.(js|css|html|png|svg|js.map)$
#fileembed pattern .*\.png



md5-f8b48fccd03599094034bf2b507e9e67



#fileembed pattern .*\.js$

Dan seterusnya..

Untuk data anekdotal: Saya tahu jika ini diterapkan, saya akan segera menggunakannya di dua tempat di tempat kerja, dan keduanya akan memberikan akses yang tidak dimodifikasi ke file teks statis. Saat ini kami menggunakan langkah //go:generate untuk mengonversi file menjadi string konstan (format heksadesimal).

Saya akan memilih paket baru sebagai lawan dari arahan. Jauh lebih mudah untuk dipahami, lebih mudah untuk ditangani/dikelola dan lebih mudah untuk didokumentasikan dan diperluas. misalnya Dapatkah Anda dengan mudah menemukan dokumentasi untuk direktif Go seperti "go:generate"? Bagaimana dengan dokumentasi paket “fmt”? Apakah Anda melihat ke mana saya pergi dengan ini?

Jadi, Go bisa saja memiliki alat sematan "resmi" sendiri yang berjalan secara default pada go build

@andreynering Saya tahu manajer paket dan alat bahasa lainnya mengizinkannya, tetapi menjalankan kode/perintah arbitrer pada waktu pembuatan adalah kerentanan keamanan (untuk apa yang saya harap adalah alasan yang jelas).

Dua hal tambahan muncul di benak saya ketika memikirkan fitur ini:

  • Bagaimana cara menyematkan file secara otomatis dengan cache build?
  • Apakah itu mencegah bangunan yang dapat direproduksi? Jika data diubah dengan cara apa pun (misalnya mengompresinya), data tersebut harus memperhitungkan reproduktifitas.

stuffbin , yang ditautkan di komentar pertama, dibuat untuk memungkinkan aplikasi web yang di-hosting-sendiri menyematkan aset statis (HTML, JS ...). Ini tampaknya menjadi kasus penggunaan umum.

Kecuali diskusi kompilasi/kompresi, masalah lain adalah kurangnya abstraksi sistem file di stdlib karena:

  • Pada mesin pengembang, banyak go run s dan build tidak perlu dibebani oleh overhead penyematan (sementara secara opsional mengompresi) aset. Abstraksi sistem file akan memungkinkan untuk dengan mudah _failover_ ke sistem file lokal selama pengembangan.

  • Aset dapat berubah secara aktif selama pengembangan, misalnya, frontend Javascript lengkap dalam aplikasi web. Kemampuan untuk beralih secara mulus antara embed dan sistem file lokal alih-alih aset yang disematkan akan memungkinkan untuk menghindari kompilasi dan menjalankan kembali biner Go hanya karena aset berubah.

Sunting: Sebagai kesimpulan, jika paket embed dapat mengekspos antarmuka seperti sistem file, sesuatu yang lebih baik daripada http.FileSystem, itu akan menyelesaikan masalah ini.

Kemampuan untuk beralih dengan mulus antara embed dan sistem file lokal

Tentunya ini bisa diimplementasikan di level aplikasi, dan di luar cakupan proposal ini, bukan?

Tentunya ini bisa diimplementasikan di level aplikasi, dan di luar cakupan proposal ini, bukan?

Maaf, baru sadar, cara saya mengucapkannya ambigu. Saya tidak mengusulkan implementasi sistem file di dalam paket embed, tetapi hanya sebuah antarmuka, sesuatu yang lebih baik dari http.FileSystem . Itu akan memungkinkan aplikasi untuk mengimplementasikan segala jenis abstraksi.

Sunting: Salah ketik.

@knadh Sangat setuju bahwa itu akan berfungsi ketika Anda hanya menggunakan go run juga, cara Packr menangani ini sangat bagus. Ia tahu di mana file Anda berada, jika tidak disematkan di aplikasi, maka ia memuatnya dari disk seperti yang diharapkan pada dasarnya sebagai "mode pengembangan".

Penulis Packr juga telah merilis alat baru Pkger yang lebih fokus pada Modul Go. Semua file di sana relatif terhadap root Modul Go. Saya sangat menyukai ide itu, tetapi Pkger tampaknya belum mengimplementasikan pemuatan pengembangan lokal dari disk. Kombinasi keduanya akan menjadi IMO yang luar biasa.

Saya tidak tahu apakah itu sudah tidak berjalan, tetapi sementara "pendekatan paket tersemat" cukup ajaib, itu juga memberikan beberapa kehebatan karena alat ini dapat menyimpulkan apa yang harus dilakukan pada file berdasarkan panggilan. misalnya API mungkin seperti

package embed
func FileReader(name string) io.Reader {…}
func FileReaderAt(name string) io.ReaderAt {…}
func FileBytes(name string) []byte {…}
func FileString(name string) string {…}

Jika alat go menemukan panggilan ke FileReaderAt , ia mengetahui bahwa data harus tidak dikompresi. Jika hanya menemukan panggilan FileReader , ia tahu bahwa ia dapat menyimpan data terkompresi. Jika ia menemukan panggilan FileBytes , ia tahu bahwa ia perlu melakukan salinan, jika ia hanya menemukan FileString , ia tahu ia dapat melayani dari memori hanya-baca. Dan seterusnya.

Saya tidak yakin ini adalah cara yang masuk akal untuk mengimplementasikan ini untuk alat go yang tepat. Tapi saya ingin menyebutkan ini, karena memungkinkan untuk mendapatkan manfaat kompresi dan penyematan nol-salinan tanpa memiliki kenop yang sebenarnya.

[ sunting ] juga, tentu saja, mari kita tambahkan hal-hal mangling ekstra ini setelah fakta, dengan fokus pada set fitur yang lebih minimal terlebih dahulu [/ sunting]

Jika hanya menemukan panggilan FileReader...

Ini akan menghalangi penggunaan metode lain melalui refleksi.

[Sunting] Sebenarnya, saya pikir implikasinya lebih luas dari itu. Jika penggunaan FileReaderAt merupakan indikasi bahwa data harus tidak dikompresi, maka penggunaan FileReaderAt() dengan input non- const menyiratkan bahwa semua file harus disimpan tanpa kompresi.

Saya tidak tahu apakah itu baik atau buruk. Saya hanya berpikir heuristik ajaib tidak akan berguna seperti yang terlihat pada awalnya.

Satu argumen yang mendukung pragma komentar ( //go:embed ) alih-alih nama direktori khusus ( static/ ): komentar memungkinkan kita menyematkan file dalam arsip pengujian untuk sebuah paket (atau arsip xtest ) tetapi bukan perpustakaan yang sedang diuji. Komentar hanya perlu muncul dalam file _test.go .

Saya berharap ini akan mengatasi masalah umum dengan modul: sulit untuk mengakses data uji untuk paket lain jika paket itu ada di modul lain. Sebuah paket dapat menyediakan data untuk pengujian lain dengan komentar seperti //go:embedglob testdata/* dalam file _test.go . Paket dapat diimpor ke biner non-tes biasa tanpa menarik file-file itu.

@fd0 ,

Bagaimana cara menyematkan file secara otomatis dengan cache build?

Itu akan tetap bekerja. Hash konten file yang disematkan akan dicampur ke dalam kunci cache.

Apakah mungkin (atau bahkan ide yang bagus) untuk memiliki modul/paket/mekanisme yang praktis transparan, seperti dari dalam aplikasi Anda, Anda cukup mencoba membuka jalur seperti

internal://static/default.css

dan fungsi File akan membaca data dari dalam biner, atau dari lokasi alternatif
mantan Package.Mount("internal[/<folder>.]", binary_path + "/resources/")

untuk membuat "internal: //" dengan semua file dalam biner, kembali ke jalur yang dapat dieksekusi / sumber daya / jika dalam mode dev atau jika file tidak ditemukan dalam biner (dan mungkin memberikan peringatan atau sesuatu untuk tujuan pencatatan)

Ini akan memungkinkan misalnya untuk memiliki

Package.Mount("internal", binary_path  + "/resources/private/")
Package.Mount("anotherkeyword", binary_path  + "/resources/content/")

Mungkin yang terbaik untuk mengunci lokasi alternatif ke jalur yang dapat dieksekusi ketika dalam mode 'rilis', tetapi santai ini dalam mode dev (hanya izinkan folder di go_path atau sesuatu seperti itu)

Secara default, paket "mount" internal:// atau kata kunci lain tetapi biarkan pengguna mengganti namanya jika dia mau.. ex .ReMount("internal",,"myCustomName") atau sesuatu seperti itu.

Hal lain ... apakah masuk akal untuk memeriksa perubahan terakhir/waktu yang dimodifikasi di lokasi alternatif dan secara otomatis menimpa file internal jika ada file seperti itu di luar aplikasi (mungkin memiliki tanda yang memungkinkan untuk ini, dapat dikonfigurasi oleh programmer sebelum membangun)
Ini mungkin diinginkan untuk patch aplikasi super cepat, di mana Anda tidak ingin menunggu build baru dibuat dan didistribusikan.. Anda bisa membuat folder dan menyalin file di sana, dan biner akan beralih ke yang baru mengajukan.

Di Windows, apakah mungkin, atau masuk akal untuk menggunakan sumber daya (seperti dalam gumpalan data biner dalam sumber daya)
Dan sedikit tidak terkait, tetapi mungkin paket ini juga dapat menangani ikon bundling dalam data yang dapat dieksekusi, atau manifes, atau bahkan mungkin sumber daya lainnya? Saya menyadari itu hanya Windows ...
Saya akan membayangkan pembangun dapat mencatat tanggal modifikasi/perubahan terakhir dari file di folder alternatif dan hanya memicu "buat gumpalan data" jika file berubah dan menyimpan gumpalan di suatu tempat.
Mungkin hanya membuat file "cache" yang dipilih pengguna untuk mengaktifkan kompresi pada file yang dibundel ini (jika akhirnya diputuskan untuk mengompresnya) ... jika kompresi dipilih, hanya file tertentu yang dimodifikasi yang harus dikompres ulang pada waktu pembuatan , file lain hanya akan disalin ke biner dari cache.

Satu masalah yang saya lihat adalah jika paket mengizinkan nama khusus, itu perlu memiliki semacam daftar hitam, seperti tidak mengizinkan "udp, file, ftp, http, https, dan berbagai kata kunci populer lainnya"

Adapun menyimpan sebagai byte array / string atau kompresi ... imho keputusan apa pun yang dibuat, itu harus meninggalkan ruang untuk dengan mudah memperbarui di masa depan ... ex Anda bisa mulai tanpa kompresi dan hanya memiliki daftar offset dan ukuran file dan nama file tetapi memungkinkan untuk dengan mudah menambahkan kompresi di masa depan (mis metode zlib, lzma, ukuran terkompresi, ukuran tidak terkompresi jika diperlukan untuk mengalokasikan cukup memori untuk membongkar potongan dll dll.

Saya pribadi akan senang jika executable dapat dikemas dengan UPX atau yang setara, saya berasumsi biner akan dibongkar dalam memori dan semuanya akan berfungsi.

Beberapa pemikiran yang berhubungan secara tidak langsung:

  • Saya suka pendekatan package embed karena menggunakan sintaks Go
  • Saya pikir kebutuhan untuk kompresi dan manipulasi lainnya bukan tentang ukuran biner, ini tentang keinginan untuk menyimpan hanya bentuk konten yang paling berbeda dalam repositori, agar tidak ada status "tidak sinkron" di mana seseorang lupa untuk membuat ulang dan komit formulir terkompresi saat mengubah "sumber", dan agar paket tetap "dapat diperoleh". Tanpa membahas poin-poin ini, kami hanya memecahkan masalah standardisasi, yang mungkin dapat diterima, tetapi tampaknya tidak ideal.
  • Saya pikir kita dapat menghindari kebutuhan rantai alat yang perlu secara aktif mendukung kompresi/transformasi tertentu jika interaksi embed secara opsional dapat menyediakan "codec". Bagaimana tepatnya mendefinisikan codec tergantung pada sintaks integrasi, tapi saya membayangkan sesuatu seperti
package embed

type Codec interface {
    // Encode transforms a source representation to an in-binary encoded asset.
    Encode(io.Writer, io.Reader) error

    // Decode transforms an in-binary asset to its active representation that the embedded application wants to use.
    Decode(io.Writer, io.Reader) error
}

Ini dapat mencakup kasus penggunaan yang sangat spesifik, seperti yang dibuat-buat ini:

package main

func NewJSONShrinker() embed.Codec {
   return jsonShrinker{}
}

type jsonShrinker struct{}
func (_ jsonShrinker)  Encode(io.Writer, io.Reader) error {
    // use json.Compact + gzip.Encode...
}
func (_ jsonShrinker)  Decode(io.Writer, io.Reader) error {
    // use gzip.Decode + json.Indent
}

Menggunakannya bisa terlihat seperti

// go:embed file.name NewJSONShrinker

func main() {
    embed.NewFileReader("file.name") // codec is implied by the comment above
}

atau mungkin

func main() {
    f, err := embed.NewFileReaderCodec("file.name", NewJSONShrinker())
    ...
}

Dalam bentuk kedua, ada komplikasi yang perlu dipahami oleh rantai alat secara statis Codec mana yang akan digunakan, karena ia harus melakukan langkah Encode pada waktu kompilasi. Jadi kita harus melarang nilai Codec apa pun yang tidak dapat ditentukan dengan mudah pada waktu kompilasi.

Mengingat dua opsi ini, saya pikir saya akan memilih komentar ajaib plus codec. Ini menghasilkan fitur yang lebih kuat yang membahas semua tujuan yang dinyatakan di sini. Plus, saya tidak berpikir komentar ajaib tidak dapat diterima di sini. Kami sudah menoleransi mereka melalui go:generate untuk tujuan ini sekarang. Jika ada, orang mungkin menganggap paket ajaib itu sendiri lebih merupakan penyimpangan dari idiom saat ini. Ekosistem Go saat ini tidak memiliki banyak fitur yang memungkinkan file sumber menginstruksikan rantai alat untuk menggunakan file sumber tambahan, dan saya pikir satu-satunya yang bukan komentar ajaib saat ini adalah kata kunci import .

Jika kita melakukan kompresi, tidak akan ada tombol jenis codec atau tingkat kompresi sama sekali. Artinya, memiliki kenop sama sekali adalah argumen terbesar untuk tidak mendukung kompresi sama sekali.

Satu-satunya pilihan yang ingin saya ungkap, jika ada, adalah: akses acak atau tidak. Jika Anda tidak memerlukan akses acak, perkakas dan runtime dapat memilih kompresi apa pun yang sesuai dan tidak memaparkannya kepada pengguna. Dan kemungkinan akan berubah/meningkat seiring waktu.

Tapi saya merasa @rsc tidak memiliki kompresi karena kesadaran yang saya miliki: konten yang paling dapat dikompresi (HTML, JS, CSS, dll) adalah konten yang Anda masih ingin akses acak (ke dilayani melalui, katakanlah, http.FileServer , yang mendukung permintaan rentang)

Dan melihat ukuran gabungan dari HTML/CSS/JS Perkeep yang kami sematkan, 48 KB tidak terkompresi. Biner server Perkeep adalah 49 MB. (Saya mengabaikan ukuran gambar yang disematkan karena sudah dikompresi.) Jadi sepertinya itu tidak sepadan, tetapi bisa ditambahkan nanti.

Dari diskusi dengan @rsc , sepertinya kita bisa melakukan campuran dari pendekatan di atas:

Dalam waktu proses paket,

package runtime

type Files struct {
     // unexported field(s), at least 1 byte long so Files has a unique address
}

func (f *Files) Open(...) (...) { ...}
func (f *Files) Stat(...) (...) { ...}
func (f *Files) EnumerateSomehow(...) { ...}

Kemudian dalam kode Anda:

package yourcode

//go:embed static/*
//go:embed logo.jpg
var website runtime.Files

func F() {
     ... = website.Open("logo.jpg")
}

Kemudian alat cmd/go akan mengurai komentar go:embed dan meng-glob pola-pola itu + hash file-file itu dan mendaftarkannya dengan runtime, menggunakan &website .

Runtime akan secara efektif memiliki peta setiap alamat File ke apa isinya dan di mana dalam file yang dapat dieksekusi itu (atau apa nama bagian ELF/etc mereka). Dan mungkin apakah mereka mendukung atau tidak mendukung akses acak, jika kita akhirnya melakukan kompresi apa pun.

@gdamore ,

Hanya kerutan lain di sini - Saya memiliki proyek berbeda yang menggunakan kode sumber DTrace (tertanam). Ini sensitif terhadap perbedaan antara n dan rn.
...
Jika berbagai munging tidak berhasil, maka itu adalah argumen untuk tidak menggunakan fasilitas baru ini.

Anda juga dapat munge saat runtime untuk menghapus carriage return yang disematkan dari pengguna Windows yang menjalankan go install. Saya telah menulis filter io.Reader itu beberapa kali.

Tapi saya merasa @rsc tidak memiliki kompresi karena kesadaran yang saya miliki: konten yang paling dapat dikompresi (HTML, JS, CSS, dll) adalah konten yang Anda masih ingin akses acak (untuk dilayani melalui, katakanlah, http.FileServer, yang mendukung permintaan jangkauan)

Kompresi dan akses acak tidak sepenuhnya eksklusif satu sama lain. Lihat misalnya beberapa diskusi di sini: https://stackoverflow.com/questions/429987/compression-formats-with-good-support-for-random-access-within-archives

Kompresi dan akses acak tidak sepenuhnya eksklusif

Ya, jika kita ingin pencarian kasar dengan beberapa overhead untuk mendapatkan posisi yang tepat. Saya melakukan beberapa pekerjaan di ruang ini dengan format stargz CRFS . Tapi saya khawatir biayanya akan cukup besar sehingga kami tidak ingin melakukannya secara otomatis untuk orang-orang. Saya kira Anda juga bisa dengan malas mengembangnya ke dalam memori (dan dapat meletakkannya di GC, seperti sync.Pool), tetapi sepertinya tidak sepadan.

Saya khawatir biaya overhead akan cukup besar sehingga kami tidak ingin melakukannya secara otomatis untuk orang-orang.

Cukup adil. Pertanyaan pentingnya adalah apakah kita akan lebih memilih API yang memungkinkan kita mengubah pikiran kita dengan murah tentang hal ini nanti, jika kebutuhan berubah atau jika eksperimen menunjukkan bahwa overhead dapat diterima.

@bradfitz poin bagus. Dan saya pasti bisa melakukannya. FWIW, di repo saya, saya juga telah mengonfigurasi git agar tidak terlalu beracun saat melihat file .d. Masih saya menemukan properti string yang disematkan dengan backquotes berguna, karena dapat diprediksi dan tidak tunduk pada keinginan git atau sistem.

Apa yang saya maksud dengan ide Codec adalah bahwa kompresi bukanlah satu-satunya transformasi yang mungkin diinginkan dan bahwa tipe Codec yang disediakan pengguna memungkinkan toolchain untuk mengabaikan flag selain "codec mana." Setiap tingkat kompresi, atau algoritme sama sekali, kompresi atau lainnya, harus spesifik untuk codec yang digunakan. Saya sepenuhnya setuju bahwa mencoba untuk "mendukung kompresi" dalam arti menyediakan beberapa set format dan kenop tertentu akan menjadi pengejaran angsa liar dengan semua variasi yang bisa diminta orang. Bahkan, saya akan sangat senang dengan penggunaan yang tidak biasa, seperti pra- pemrosesan data i18n, mungkin, atau memproses kumpulan data seperti di

Saya memang memikirkan cara lain untuk memberikan fleksibilitas yang sama yang mungkin lebih menyenangkan. Direktif // go:embed dapat berupa pemanggilan perintah, seperti halnya // go:generate . Untuk kasus yang paling sederhana, sesuatu seperti

// go:embed "file.name" go run example.com/embedders/cat file.name

Perbedaan utamanya adalah, tentu saja, bahwa stdout dari pemanggilan perintah disematkan di bawah nama yang diberikan. Contoh ini juga menggunakan paket pura-pura dengan go run untuk menunjukkan bagaimana hal itu mungkin dilakukan untuk membuat perintah OS independen, karena cat mungkin tidak tersedia di semua tempat kompilasi Go.

Itu menangani langkah "encode" dari transformasi, dan mungkin tugas langkah "decode" dapat diserahkan kepada pengguna. Paket runtime/embed hanya dapat menyediakan byte yang diminta pengguna untuk disematkan rantai alat, apa pun penyandiannya. Ini bagus karena pengguna tahu seperti apa proses decoding yang seharusnya.

Satu kelemahan besar dari ini adalah saya tidak melihat cara yang baik untuk menyematkan gumpalan banyak file dengan cara ini, di luar byte yang disematkan menjadi Zip atau sesuatu. Itu mungkin sebenarnya cukup baik, karena glob masih dapat digunakan oleh perintah Zip, dan itu adalah sisi yang menentukan di mana Anda benar-benar peduli dengan glob. Tetapi kami juga dapat memiliki dua fitur dari proposal ini, satu untuk melakukan penyematan sederhana dan yang lainnya untuk menjalankan penyematan generator.

Satu kelemahan yang mungkin terjadi pada saya adalah ia menambahkan langkah terbuka ke dalam build, dengan asumsi embed harus ditangani oleh go build dan tidak memerlukan permintaan toolchain tambahan seperti yang dilakukan go generate . Saya pikir tidak apa-apa, meskipun. Mungkin alat tersebut diharapkan dapat mengelola cache-nya sendiri untuk menghindari pengulangan operasi yang mahal, atau mungkin dapat berkomunikasi dengan rantai alat untuk menggunakan cache Go. Kedengarannya seperti masalah yang dapat dipecahkan dan sesuai dengan keseluruhan tema go build melakukan lebih banyak untuk kami (seperti mengambil modul).

Apakah salah satu tujuan proyek ini untuk memastikan build Go tidak memerlukan alat eksternal dan tidak ada baris go:generate?

Jika tidak, tampaknya ada baiknya menjaga hal-hal sederhana dan hanya mendukung irisan byte atau string karena jika pengguna menginginkan kompresi dengan banyak kenop, mereka dapat melakukannya di file make (atau serupa), buat baris, dll. sebelum membangun, jadi sepertinya tidak ada gunanya menambahkannya ke apa pun hasil dari proposal ini.

Jika tidak memerlukan Make atau sejenisnya adalah tujuan, maka saya kira mungkin masuk akal untuk menggunakan kompresi, tetapi secara pribadi saya akan segera menggunakan Make, go generate, dll. untuk melakukan kompresi kemudian tetap embed sederhana dan hanya menyematkan beberapa byte .

@SamWhited ,

Apakah salah satu tujuan proyek ini untuk memastikan build Go tidak memerlukan alat eksternal dan tidak ada baris go:generate?

Ya.

Jika orang ingin menggunakan go:generate atau Makefiles atau alat lain, mereka memiliki lusinan pilihan hari ini.

Kami menginginkan sesuatu yang portabel dan aman serta benar yang berfungsi secara default. (dan untuk lebih jelasnya: safe berarti kita tidak dapat menjalankan kode arbitrer pada waktu "go install", untuk alasan yang sama bahwa go:generate tidak berjalan secara default)

@stephens2424

Saya pikir kita dapat menghindari kebutuhan rantai alat yang perlu secara aktif mendukung kompresi/transformasi tertentu jika interaksi sematan dapat secara opsional menyediakan "codec".

Tidak ada eksekusi kode arbitrer selama go build .

Tidak ada eksekusi kode arbitrer selama go build.

Ya, saya melihat itu sekarang. Saya kira tidak ada cara untuk mendamaikan hanya memiliki file "sumber" yang dikomit ke repo, ingin file "diproses" disematkan, agar paketnya "dapat diambil", _and_ menjaga go build sederhana dan aman. Saya masih mendorong untuk standardisasi di sini, tapi saya berharap untuk memiliki kue saya dan memakannya juga, saya kira. Layak dicoba! Terima kasih telah menangkap masalahnya!

@tipis

Ini akan menghalangi penggunaan metode lain melalui refleksi.

Tidak ada metode dalam apa yang saya sebutkan, hanya fungsi. Mereka tidak dapat ditemukan saat runtime dan tidak ada cara untuk merujuknya tanpa menyebutkan namanya di sumber. Dan perhatikan bahwa nilai antarmuka yang dikembalikan oleh fungsi yang berbeda tidak harus tipe yang sama - memang saya berharap mereka menjadi tipe yang tidak diekspor dengan persis metode yang diperlukan untuk mengimplementasikan antarmuka itu atau turunan dari *strings.Reader dll., apa pun yang masuk akal dalam konteksnya.

Namun, bisa dibilang, ide tersebut menderita karena menyebarkan fungsi yang diekspor dari paket embed sebagai nilai. Meskipun kemungkinan itu tidak akan menjadi masalah - tanda tangan berisi tipe yang tidak diekspor (lihat di bawah), jadi Anda tidak dapat mendeklarasikan variabel, argumen, atau pengembalian tipenya. Anda dapat meneruskannya ke reflect.ValueOf sendiri, secara teori. Saya bahkan tidak tahu apakah itu akan memungkinkan Anda untuk benar-benar memanggil mereka (Anda masih harus membuat nilai tipe parameter mereka, yang tidak diekspor. Entahlah jika refleksi memungkinkan itu).

Tetapi bagaimanapun juga: Masih mungkin (dan paling sederhana) untuk pesimis jika ada fungsi tingkat atas embed digunakan sebagai nilai dan menganggap batasan yang dibuatnya pada semua file yang disematkan. Artinya, jika Anda memutuskan untuk melakukan hal -

Sebenarnya, menurut saya implikasinya lebih luas dari itu. Jika penggunaan FileReaderAt merupakan indikasi bahwa data harus tidak dikompresi, maka penggunaan FileReaderAt() dengan input non-const menyiratkan bahwa semua file harus disimpan tanpa kompresi.

Tidak masuk akal untuk mengizinkan input non-const, karena nama file perlu diketahui secara statis untuk melakukan penyematan. Saya tidak tepat menggunakan string sebagai jenis parameter nama file: Mereka seharusnya benar-benar type filename string tidak diekspor dan tidak digunakan sebagai apa pun selain argumen fungsi. Dengan begitu, tidak mungkin melewatkan apa pun yang bukan merupakan konstanta string yang tidak diketik.

@Merovius

Tidak masuk akal untuk mengizinkan input non-const

Saya pikir kita sedang membicarakan hal yang berbeda. Maksud saya input ke fungsi pengakses (yaitu FileReaderAt() ) Saya yakin Anda akan setuju bahwa input non-const masuk akal di sana.

Dan maksud saya adalah: Misalkan kita telah menyematkan 100 file, tetapi kita memiliki panggilan FileReaderAt(filename) , di mana filename tidak konstan; tidak ada cara untuk mengetahui mana (jika memang ada) file yang disematkan yang akan diakses dengan cara ini, jadi semua harus disimpan tanpa kompresi.

@flimzy kami membicarakan hal yang sama, saya benar-benar tidak berpikir nama file non-const akan masuk akal :) Yang, memikirkannya, salah dan kelalaian. Maaf tentang itu. Fasilitas untuk glob atau memasukkan seluruh direktori dan kemudian mengulanginya sebenarnya cukup penting, ya. Masih berpikir ini bisa diselesaikan - misalnya dengan membuat keputusan per koleksi (dir / gumpal) dan hanya mengizinkan untuk memilih orang-orang dengan nama konstan - tapi seperti yang saya katakan: Ini tidak benar-benar sebuah API saya akan mempertimbangkan yang super tepat untuk alat Go karena betapa ajaibnya itu. Jadi pergi ke gulma seperti ini mungkin memberi konsep lebih banyak ruang dalam diskusi daripada yang seharusnya :)

Kasus lain yang tidak saya lihat di pesan sebelumnya dan yang membuat saya mempertimbangkan untuk menyematkan file ke dalam biner Go adalah ketidakmungkinan untuk mendistribusikan paket pembungkus perpustakaan bersama C dengan benar menggunakan go build/install biasa (perpustakaan bersama tetap berada di sumber).

Saya tidak melakukannya pada akhirnya tetapi itu pasti akan membuat saya mempertimbangkannya kembali untuk kasus ini. Pustaka C memang memiliki banyak dependensi yang akan lebih mudah didistribusikan sebagai pustaka bersama. Pustaka bersama ini dapat disematkan oleh binding Go.

Wow!!!

@Julio-Guerra
Saya cukup yakin Anda masih harus mengekstraknya ke disk dan kemudian menggunakan dlopen dan dlsym untuk memanggil fungsi C.

Sunting: Sedikit salah memahami posting Anda, baru sadar bahwa Anda sedang berbicara tentang membuat biner untuk distribusi

Di luar aset statis http, untuk gumpalan yang disematkan yang Anda perlukan dalam pointer ke dalam memori, alangkah baiknya memiliki fungsi yang mengembalikan pointer ke memori tertanam yang sudah dalam proses. Jika tidak, seseorang harus mengalokasikan memori baru dan membuat salinan dari io.Reader. Itu akan menghabiskan dua kali memori.

@gliserin , sekali lagi, itu adalah string . string adalah pointer dan panjang.

Bukankah lebih bagus untuk memiliki beberapa cara untuk menandai kode yang akan dieksekusi pada waktu kompilasi dan memberikan hasilnya saat runtime. Dengan begitu Anda dapat membaca file apa pun, mengompresnya jika Anda suka pada waktu kompilasi dan saat runtime Anda dapat mengaksesnya. Itu akan berfungsi untuk beberapa perhitungan karena akan berfungsi untuk memuat konten file sebelumnya.

@burka seperti yang dikatakan sebelumnya di utas, go build tidak akan menjalankan kode arbitrer.

@burka , itu secara eksplisit di luar jangkauan. Keputusan itu (tidak ada eksekusi kode pada waktu kompilasi) dibuat sejak lama dan ini bukan bug untuk mengubah kebijakan itu.

Efek samping dari proposal ini adalah bahwa proksi go tidak pernah dapat mengoptimalkan file yang mereka simpan menjadi hanya file go. Proxy harus menyimpan seluruh repositori karena ia tidak akan tahu apakah kode Go menyematkan file non-Go.

Saya tidak tahu apakah proxy sudah mengoptimalkan untuk ini tetapi mungkin suatu hari nanti mereka mau.

@leighmcculloch Saya rasa ini juga tidak terjadi hari ini. Semua file non-Go dalam paket Go harus disertakan dalam arsip modul, karena mungkin diperlukan untuk go test . Anda mungkin juga memiliki file C untuk cgo, sebagai contoh lain.

Ini adalah arah yang menarik, kami pasti membutuhkannya untuk kasus penggunaan kami.

Yang mengatakan, saya merasa seperti ada kasus penggunaan yang berbeda dengan persyaratan yang berbeda tetapi kebanyakan orang yang berkomentar tentang _bagaimana_ mereka pikir itu harus dilakukan secara implisit membayangkan kasus penggunaan mereka sendiri tetapi tidak secara eksplisit mendefinisikannya.

Mungkin akan membantu — setidaknya sangat membantu bagi saya — jika kita dapat menggambarkan kasus penggunaan yang berbeda untuk solusi penyematan file dan tantangan yang diberikan oleh setiap kasus penggunaan.

Misalnya, kasus penggunaan utama kami adalah menyematkan HTML+CSS+JS+JPG+dll sehingga ketika aplikasi go dijalankan, aplikasi tersebut dapat menulis file tersebut ke direktori sedemikian rupa sehingga dapat dilayani oleh http.FileServer . Mengingat bahwa kasus penggunaan sebagian besar komentar yang saya baca membahas Pembaca dan Penulis asing bagi saya karena kami tidak perlu mengakses file dari Go, kami hanya membiarkan go-bindata menyalinnya ke disk _(walaupun mungkin ada cara untuk memanfaatkan teknik yang lebih baik yang belum kita sadari harus kita pertimbangkan.)_

Namun tantangan kami adalah sebagai berikut: Kami biasanya menggunakan GoLand dengan debuggernya dan akan bekerja di aplikasi web membuat perubahan terus-menerus. Jadi selama pengembangan kita membutuhkan http.FileServer untuk memuat file langsung dari direktori sumber kita. Tetapi ketika aplikasi berjalan http.FileServer perlu membaca file-file itu dari direktori tempat file telah ditulis oleh solusi penyematan. Artinya ketika kita mengkompilasi kita harus menjalankan go-bindata untuk memperbarui file, dan kemudian memeriksanya ke Git. Dan itu semua umumnya bisa diterapkan dengan go-bindata , meskipun tentu saja bukan ide.

Namun di lain waktu kita perlu benar-benar menjalankan executable yang dikompilasi, sehingga kita dapat melampirkan debugger ke program yang sedang berjalan dan masih memiliki program yang memuat file dari direktori sumber dan bukan dari direktori tempat file yang disematkan ditulis oleh go-bindata . Saat ini kami tidak memiliki solusi yang baik untuk ini.

Jadi itulah kasus penggunaan dan tantangan kami. Mungkin orang lain dapat secara eksplisit mendefinisikan kasus penggunaan lain dan serangkaian tantangan terkait, sehingga diskusi ini dapat secara eksplisit membahas berbagai ruang masalah dan/atau secara eksplisit menunjukkan bahwa upaya ini tidak akan menjawab kebutuhan spesifik dari ruang masalah yang diberikan?

Terima kasih sebelumnya untuk mempertimbangkan.

Karena saya tidak melihatnya disebutkan sebagai kasus penggunaan, kami juga mendapat manfaat dari ini untuk direktori template kami yang kami akses melalui template.ParseFiles.

Saya akan menemukan pendekatan terbersih sebagai metode ikut serta melalui go.mod . Ini akan memastikan itu kompatibel ke belakang (karena proyek yang ada harus memilih untuk menggunakannya) dan memungkinkan perkakas (seperti proksi go) untuk menentukan file apa yang diperlukan. Perintah go mod init dapat diperbarui untuk menyertakan versi default untuk proyek baru agar lebih mudah digunakan di masa mendatang.

Saya dapat melihat argumen untuk membuat direktori menjadi nama standar (jika kami memerlukan opt-in maka itu bisa menjadi nama yang lebih bersih/sederhana) atau membuat nama direktori didefinisikan dalam go.mod itu sendiri dan memungkinkan pengguna untuk pilih nama (tetapi memiliki default yang disediakan oleh go mod init .

Dalam pikiran saya, solusi seperti ini mencapai keseimbangan dalam kemudahan penggunaan dan lebih sedikit "keajaiban".

@jayconrod menulis:

Satu argumen yang mendukung pragma komentar (//go:embed) alih-alih nama direktori khusus (static/): komentar memungkinkan kita menyematkan file dalam arsip pengujian untuk sebuah paket (atau arsip xtest) tetapi bukan perpustakaan sedang diuji.

Ini adalah pengamatan yang sangat bagus. Meskipun jika kita ingin menggunakan nama direktori khusus, kita dapat menggunakan mekanisme yang sudah dikenal: static untuk semua build, static_test untuk build uji, static_amd64 untuk build amd64, dan segera. Saya tidak melihat cara yang jelas untuk memberikan dukungan tag build yang sewenang-wenang.

Mungkin ada file manifes di direktori statis (default ketika diberikan manifes kosong adalah menyertakan semuanya kecuali manifes) yang menyertakan gumpalan dan memungkinkan menentukan tag build dan mungkin kompresi nanti, dll.

Satu keuntungannya adalah jika daftar go menyentuh direktori yang berisi manifes, ia dapat melewati pohon itu ala #30058

Satu kelemahannya adalah itu bisa menjadi sangat htacces dan tidak, terima kasih

Mekanisme zero-knob sederhana untuk menggabungkan file ke dalam sebuah paket dapat berupa direktori khusus go.files dalam direktori paket (mirip dengan go.mod dalam sebuah modul). Akses akan dibatasi ke paket itu, kecuali jika memilih untuk mengekspor simbol.

Sunting: proposal runtime/files fungsi tunggal:

package files

func Open(name string) (io.ReadCloser, error) {
    // runtime opens embedded file based on caller package
    return rc, nil
}
package foo

import "runtime/files"

func ReadPackageFile(name string) ([]byte, error) {
    rc, err := files.Open(name)
    if err != nil {
        return nil, err
    }
    defer rc.Close()
    return ioutil.ReadAll(rc)
}

Pendekatan import "C" telah menetapkan preseden untuk jalur impor "ajaib". IMO itu berhasil dengan cukup baik.

Karena saya tidak melihatnya disebutkan sebagai kasus penggunaan, kami juga mendapat manfaat dari ini untuk direktori template kami yang kami akses melalui template.ParseFiles.

Ada tantangan lain: sementara biner mungkin berisi semua file yang diperlukan, file-file itu akan menjadi default yang saya sediakan oleh pengembang. Namun, template seperti misalnya jejak atau kebijakan privasi harus dapat disesuaikan oleh pengguna akhir. Sejauh yang saya lihat itu berarti harus ada beberapa cara untuk mengekspor file default saya dan kemudian membiarkan biner menggunakan file yang disesuaikan saat runtime, atau beberapa cara untuk mengganti versi yang disematkan dengan yang disesuaikan.

Saya pikir itu bisa dilakukan dengan menyediakan API dengan fungsi untuk 'mengekspor' dan 'mengganti' sumber daya yang disematkan. Pengembang kemudian dapat memberikan beberapa opsi baris perintah kepada pengguna akhir (menggunakan panggilan API yang disebutkan secara internal).

Semua ini, tentu saja, berdasarkan asumsi bahwa sebenarnya akan ada semacam penyematan yang pasti akan memudahkan penerapannya.

Terima kasih telah membuka masalah. Di tempat kerja kami telah memikirkan ide fitur yang sama, karena kami membutuhkan penyematan file di hampir semua proyek Golang. Pustaka yang ada berfungsi dengan baik, tetapi saya pikir ini adalah fitur yang diinginkan Golang. Ini adalah bahasa yang dibuat untuk berubah menjadi biner statis tunggal. Ini harus mencakup itu dengan mengizinkan kami memuat file aset yang diperlukan ke dalam biner, dengan API yang ramah pengembang dan universal.

Saya hanya ingin memberikan detail implementasi favorit saya dengan cepat. Banyak orang telah berbicara tentang menyediakan API secara otomatis untuk membaca file yang disematkan, alih-alih membutuhkan sinyal lain seperti komentar ajaib. Saya pikir itu harus menjadi cara untuk pergi, karena ia menawarkan sintaks terprogram yang akrab dengan pendekatan. Pergi untuk paket khusus, mungkin runtime/embed seperti yang disebutkan sebelumnya, akan memuaskan itu dan itu akan memungkinkan untuk diperpanjang dengan mudah di masa depan. Implementasi seperti berikut ini paling masuk akal bagi saya:

type EmbedPackage interface {
    Bytes(filename string) []bytes
    BytesCompressed(filename string, config interface{}) []bytes // compressed in-binary as configured by some kind of config struct, memoizes decompression during runtime on first access
    Reader(filename string) io.Reader
    File(filename string) os.File // readonly and contains all metadata
    Dir(filepath string) []os.File 
    Glob(pattern string) []os.File // like filepath.Glob()

    // maybe? this could allow to load JSON, YAML, INI, TOML, etc files more easily
    // but would probably be too much for the std lib implementation
    Unmarshal(filename string, config interface{}, ptr interface{}) 
}

Menggunakan paket itu di suatu tempat dalam kode Anda harus memicu kompiler untuk menyediakan file itu ke runtime dengan menyematkannya secara otomatis.

// embed a file that is compressed in-binary and automatically decompressed on first access
var LongText = embed.BytesCompressed("legal.html", embed.Config{ Compression: "gzip", CompressionLevel: "9" })

// loads a single file as reader for easy access
var FewLinesOfText = bufio.NewReader(embed.Reader("lines.txt"))
for _, line := range FewLinesOfText.ReadLines() { ... }

// embeds all files in the directory
var PdfFontFiles = embed.Dir("/fonts")

// unmarshals file into custom config
var PdfProcessingConfig MyPdfProcessingConfig
embed.Unmarshal("/pdf_conversion.json", embed.Config{ Encoding: "text/json" }, &PdfProcessingConfig)

Saya juga berpikir bahwa masalah keamanan dan reproduktifitas seharusnya tidak menjadi masalah jika kita membatasi impor ke file pada atau mungkin 1 tingkat direktori di bawah direktori go.mod, yang juga telah disebutkan sebelumnya di utas. Jalur sematan absolut akan diselesaikan relatif terhadap level directoy itu.

Gagal mengakses file selama proses kompilasi akan menghasilkan kesalahan kompiler.

Ini juga memungkinkan untuk membuat arsip zip di belakang biner, sehingga dapat secara efektif menjadi biner yang mengekstraksi sendiri. Mungkin ini berguna dalam beberapa kasus penggunaan? Lakukan ini sebagai percobaan di sini: https://github.com/sanderhahn/gozip

Go sudah memiliki "testdata". Tes unit menggunakan IO biasa untuk melakukan apa pun yang ingin mereka lakukan. Lingkup pengujian berarti konten tidak dikirimkan. Hanya itu yang perlu diketahui, tanpa embel-embel, tanpa keajaiban, tanpa logika wadah terkompresi, tanpa tipuan yang dapat dikonfigurasi, tanpa META-INF. Cantik, sederhana, elegan. Mengapa tidak memiliki folder "data" untuk dependensi lingkup runtime yang dibundel?

Kami dapat dengan mudah memindai proyek Go yang ada di Github ea dan menghasilkan sejumlah proyek yang sudah menggunakan folder "data", dan karenanya memerlukan adaptasi.

Hal lain yang tidak jelas bagi saya. Untuk pembahasan direktori static tidak 100% jelas bagi saya jika kita membahas direktori static _source_ , atau direktori static tempat file akan tersedia _at runtime_ ?

Dan perbedaan ini sangat penting karena berkaitan dengan proses pengembangan dan kode debug yang sedang dikembangkan.

@mikeschinkel cukup jelas dari pos asli bahwa penyematan akan terjadi pada waktu pembuatan:

buat go install / go build melakukan penyematan secara otomatis

Posting asli, dan beberapa komentar di atas, juga membahas memiliki mode "dev" untuk memuat file saat run-time.

@mvdan Terima kasih atas jawabannya. Jadi menurut Anda ini berarti direktori /static/ yang diusulkan akan relatif terhadap root repo aplikasi, repo paket, dan/atau mungkin keduanya?

Dan lokasi file runtime itu akan sepenuhnya bergantung pada di mana pengembang ingin menempatkannya?

Jika itu semua benar — dan tampaknya logis — akan sangat membantu jika program yang dikompilasi dengan informasi debug dapat secara opsional memuat file dari lokasi sumbernya untuk memfasilitasi debugging tanpa banyak logika dan kode tambahan — dan tidak standar.

Beberapa orang di atas proksi modul yang disebutkan. Saya pikir ini adalah tes lakmus yang bagus untuk desain yang baik dari fitur ini.

Tampaknya hari ini, tanpa mengeksekusi kode pengguna, untuk mengimplementasikan proxy modul yang bisa diterapkan hari ini yang menghapus file yang tidak digunakan dalam build. Beberapa desain di atas berarti modul proxy harus mengeksekusi kode pengguna untuk mengetahui file statis mana yang juga disertakan.

Orang-orang juga menyebut go.mod sebagai pilihan.

Ide: spesifikasi dalam file go.mod? Membuatnya mudah untuk diurai oleh alat lain.

module github.com/foo/bar

data internal/static ./static/*.tmpl.html

Ini akan membuat paket pada waktu kompilasi dengan data file. Sintaks glob mungkin bagus di sini, tetapi mungkin menyederhanakan dan hanya menyematkan direktori sudah cukup baik. (Selain: +1 untuk sintaks glob ** .)

import "github.com/foo/bar/internal/static"

f, err := static.Open("static/templates/foo.tmpl")

Sesuatu seperti StripPrefix mungkin bagus di sini tetapi tidak perlu. Mudah untuk membuat paket pembungkus yang menggunakan jalur file apa pun yang Anda inginkan.

Lebih lanjut dapat disederhanakan:

module github.com/foo/bar

data ./static/*.tmpl.html
import "runtime/moddata"

moddata.Open("static/foo.tmpl")

Tetapi agak tidak intuitif bahwa moddata akan memiliki perilaku yang berbeda tergantung pada paket/modul pemanggil. Akan lebih sulit untuk menulis helper (misalnya, http.Filesystem converter)

Tampaknya hari ini, tanpa mengeksekusi kode pengguna, untuk mengimplementasikan proxy modul yang bisa diterapkan hari ini yang menghapus file yang tidak digunakan dalam build. Beberapa desain di atas berarti modul proxy harus mengeksekusi kode pengguna untuk mengetahui file statis mana yang juga disertakan.

Saya tidak berpikir akan ada perubahan signifikan di sini. Secara khusus, kode-C sudah dapat menyertakan file apa pun di pohon, jadi proxy modul yang ingin melakukan ini perlu mengurai C. Sepertinya pada saat itu, komentar ajaib atau API apa pun yang kami perkenalkan akan menjadi langkah kecil.

Beberapa desain di atas berarti modul proxy harus mengeksekusi kode pengguna untuk mengetahui file statis mana yang juga disertakan.

Saya pikir cukup jelas bahwa "alat go tidak boleh mengeksekusi kode pengguna selama pembuatan" adalah garis yang ditarik di pasir yang tidak akan dilintasi di sini. Dan jika alat go tidak dapat mengeksekusi kode pengguna, maka harus dimungkinkan untuk memberi tahu file apa yang akan disertakan tanpanya.

Saya telah mencoba untuk menyingkat berbagai pemikiran saya tentang kasus penggunaan ini menjadi sesuatu yang meyakinkan, dan +1 besar untuk apa yang disarankan @broady . Saya pikir itu sebagian besar merangkum apa yang saya pikirkan. Namun, saya pikir kata kuncinya harus berupa kata kerja embed alih-alih kata benda data .

  1. File yang disematkan terasa seperti sesuatu yang harus diimpor vs. hanya memiliki komentar khusus atau paket ajaib. Dan dalam proyek Go, file go.mod adalah tempat pengembang dapat menentukan modul/file yang diperlukan, jadi masuk akal untuk memperluasnya untuk mendukung penyematan.

  2. Lebih lanjut, kumpulan file yang disematkan menurut saya akan lebih berharga dan dapat digunakan kembali jika paket yang dapat disertakan daripada sesuatu yang ditambahkan secara ad-hoc ke proyek Go menggunakan sintaks satu kali. Idenya di sini adalah jika embed diimplementasikan sebagai paket, maka orang dapat mengembangkan dan membagikannya melalui Github dan orang lain dapat menggunakannya dalam proyek mereka. Bayangkan paket yang dikelola komunitas dan gratis untuk digunakan di GitHub yang berisi:

    A. File untuk negara di mana setiap file berisi semua kode pos di negara itu,
    B. File dengan semua string agen pengguna yang dikenal untuk mengidentifikasi browser,
    C. Gambar bendera setiap negara di dunia,
    D. Info bantuan mendalam yang menjelaskan kesalahan yang umum terjadi dalam program Go,
    e. dan seterusnya...

  3. Skema URL baru seperti goembed:// — atau mungkin yang sudah ada — yang dapat digunakan untuk membuka dan membaca file dari paket sehingga memungkinkan _(all?)_ API manipulasi file yang ada untuk dimanfaatkan alih-alih membuat yang baru yang, sesuatu seperti berikut ini yang akan menjadi relatif terhadap embed yang terkandung dalam paket saat ini:

    data, err := ioutil.ReadFile("goembed://postal-codes.txt")    
    if (err != nil) {
      fmt.Println(err)
    }
    

Dengan konsep di atas, tidak ada yang terasa seperti _"ajaib"_; semuanya akan ditangani secara elegan dengan mekanisme yang terasa seperti itu dimaksudkan untuk tujuan tertentu. Perlu sedikit ekstensi yang dibutuhkan; satu kata kerja baru di go.mod dan satu skema URL baru yang akan dikenali secara internal oleh Go. Segala sesuatu yang lain akan disediakan apa adanya dari Go.

Apa yang saya lakukan sekarang?

Saya menggunakan code.soquee.net/pkgzip untuk ini sekarang (ini adalah garpu statik yang mengubah API untuk menghindari keadaan global dan mengimpor efek samping). Alur kerja normal saya (setidaknya dalam aplikasi web) adalah menyematkan aset yang dibundel dalam file ZIP kemudian menyajikannya menggunakan golang.org/x/tools/godoc/vfs/zipfs dan golang.org/x/tools/godoc/vfs/httpfs .

pergi:sematkan pendekatan

Ada dua hal yang terbukti mencegah saya mengadopsi pendekatan go:embed :

  1. Kode yang dihasilkan tidak akan muncul di dokumentasi
  2. Aset mungkin tersebar di seluruh basis kode (ini berlaku untuk menggunakan alat eksternal dan go:generate juga, itulah sebabnya saya biasanya lebih suka menggunakan makefile untuk menghasilkan berbagai set aset sebelum membangun, lalu saya dapat melihat semuanya di makefile)

Ada juga masalah yang tidak saya sertakan di atas karena itu mungkin fitur untuk beberapa yang memiliki aset menjadi bagian dari sebuah paket (sebagai lawan dari seluruh modul) berarti bahwa semua kompleksitas tag build, paket pengujian, dll .. berlaku untuk mereka, kita perlu cara untuk menentukan apakah mereka publik atau pribadi untuk paket itu, dll. Ini sepertinya banyak kompleksitas build tambahan.

Hal yang saya sukai adalah bahwa perpustakaan dapat ditulis yang hanya membuat mengimpor aset lebih mudah. Misalnya. perpustakaan dengan satu file Go yang hanya menyematkan font, atau beberapa ikon dapat diterbitkan dan saya dapat mengimpornya seperti paket go lainnya. Di masa mendatang, saya bisa mendapatkan font ikon hanya dengan mengimpornya:

import "forkaweso.me/forkawesome/v2"

pendekatan paket embed

Sementara saya menyukai gagasan untuk membuat ini semua menjadi eksplisit, kode Go normal, saya benci gagasan bahwa ini akan menjadi paket ajaib lain yang tidak dapat diimplementasikan di luar perpustakaan standar.

Apakah paket seperti itu akan didefinisikan sebagai bagian dari spesifikasi bahasa? Jika tidak di tempat lain, kode Go akan pecah di antara implementasi yang berbeda, yang juga terasa buruk. Saya mungkin akan terus menggunakan alat eksternal untuk mencegah kerusakan ini.

Juga, seperti yang telah disebutkan orang lain, fakta bahwa ini dilakukan pada waktu pembuatan berarti paket ini hanya dapat mengambil literal string atau konstanta sebagai argumen. Saat ini tidak ada cara untuk mewakili ini dalam sistem tipe, dan saya menduga itu akan menjadi titik kebingungan. Ini dapat diselesaikan dengan memperkenalkan sesuatu seperti fungsi konstan, tetapi sekarang kita berbicara tentang perubahan bahasa utama yang menjadikannya non-starter. Saya tidak melihat cara yang baik untuk memperbaiki ini sebaliknya.

Hibrida

Saya suka ide pendekatan hybrid. Alih-alih menggunakan kembali komentar (yang akhirnya tersebar di semua tempat, dan, pada catatan pribadi, hanya merasa kotor), saya ingin melihat semua aset diletakkan di satu tempat, kemungkinan file go.mod seperti yang lain telah berkata:

module forkaweso.me/forkawesome/v2

go 1.15

embed (
    fonts/forkawesome-webfont.ttf
    fonts/forkawesome-webfont.woff2
)

Ini berarti aset tidak dapat disertakan atau dikecualikan oleh tag build arbitrer, atau dalam paket arbitrer (mis. paket _testing) tanpa membuat modul terpisah. Saya pikir pengurangan kompleksitas ini mungkin diinginkan (tidak ada tag build yang disembunyikan di perpustakaan yang Anda coba impor, dan Anda tidak tahu mengapa Anda tidak memiliki aset yang tepat karena mengimpor perpustakaan seharusnya menyematkannya) , tapi YMMV. Jika ini diinginkan, komentar seperti pragma masih dapat digunakan kecuali bahwa mereka tidak menghasilkan kode dan sebagai gantinya menggunakan pendekatan yang sama seperti yang akan saya jelaskan untuk versi go.mod .

Berbeda dengan proposal asli, ini tidak akan menghasilkan kode apa pun. Sebaliknya, fungsionalitas untuk mis. membaca bagian data file ELF (atau bagaimanapun ini akhirnya disimpan di OS apa pun yang Anda gunakan) akan ditambahkan jika sesuai (mis. os atau debug/elf , dll.) dan kemudian, secara opsional, paket baru akan dibuat yang berperilaku persis seperti paket yang dijelaskan dalam OP kecuali bahwa alih-alih menjadi ajaib dan melakukan penyematan itu sendiri, paket itu hanya membaca file yang disematkan (artinya dapat diimplementasikan di luar pustaka standar Jika diinginkan).

Ini mengatasi masalah seperti harus membatasi paket ajaib untuk hanya mengizinkan string literal sebagai argumen, tetapi berarti lebih sulit untuk memeriksa apakah aset yang disematkan benar-benar digunakan di mana saja atau akhirnya menjadi bobot mati. Itu juga menghindari ketergantungan baru antara paket pustaka standar, karena satu-satunya paket yang perlu mengimpor tambahan apa pun adalah paket baru itu sendiri.

var IconFont = embed.Dir("forkaweso.me/forkawesome/v2/fonts/")
var Logo = embed.File("images/logo.jpg")

Seperti yang terlihat di atas, menempatkan sumber daya dalam modul masih dapat mencakupnya ke modul tertentu jika diinginkan. API yang sebenarnya dan cara Anda memilih aset mungkin perlu beberapa pekerjaan.

Ide lain: Daripada menambahkan jenis kata kerja baru embed di go.mod , kita bisa memperkenalkan jenis paket baru, paket data, yang diimpor dan digunakan di go.mod di cara biasa. Berikut sketsa manusia jerami.

Jika sebuah paket berisi tepat satu file .go , static.go , dan file itu hanya berisi komentar dan klausa paket, maka paket adalah paket data. Saat diimpor, cmd/go mengisi paket dengan fungsi yang diekspor yang menyediakan akses ke file yang ada di dalamnya, yang disematkan dalam biner yang dihasilkan.

Jika ini adalah paket yang sebenarnya, itu berarti aturan internal akan berlaku dan kita dapat memiliki kontrol akses tanpa menambahkan ke API.

Bagaimana dengan secara otomatis memasukkan semua file dan sub folder non .go (mengikuti aturan tidak ada kode aktual) di direktori?

Jika sebuah paket berisi tepat satu .go file, static.go , dan file itu hanya berisi komentar dan klausa paket, maka paket adalah paket data.

Apakah pemeriksaan ini akan dilakukan sebelum penerapan tag build? Jika demikian, itu sepertinya kasus khusus lainnya, yang mungkin ingin dihindari. Jika tidak, maka sangat mungkin sebuah paket dapat dilihat sebagai paket Go standar untuk beberapa tag build, dan sebagai paket data untuk yang lain. Kelihatannya aneh, tapi mungkin itu yang diinginkan?

@tipis
Itu agak memungkinkan seseorang untuk menggunakan file yang disematkan dengan satu tag dan mendefinisikan fns/vars yang sama dengan paket yang dihasilkan dan menyajikan file dengan cara lain (mungkin jarak jauh?) dengan tag lain.

Akan lebih baik jika ada flag build untuk menghasilkan fungsi wrapper sehingga kita hanya perlu mengisi bagian yang kosong.

@josharian

Jika sebuah paket berisi tepat satu .go file, static.go , dan file itu hanya berisi komentar dan klausa paket, maka paket adalah paket data.

Saya dapat membayangkan paket "data" memiliki fungsi khusus domain sendiri, seperti pencarian kode pos. Pendekatan yang baru saja Anda usulkan akan melarang apa pun selain data mentah, dan dengan demikian meniadakan manfaat untuk dapat mengemas logika dengan data.

Saya dapat membayangkan paket "data" memiliki fungsi khusus domain sendiri, seperti pencarian kode pos.

Anda dapat mengekspos fungsionalitas di my.pkg/postalcode dan meletakkan data di my.pkg/postalcode/data (atau my.pkg/postalcode/internal/data).

Saya melihat daya tarik untuk melakukannya seperti yang Anda sarankan, tetapi itu menimbulkan banyak pertanyaan: Bagaimana cara kerja kompatibilitas mundur? Bagaimana Anda menandai paket data seperti itu? Apa yang Anda lakukan jika paket tersebut memiliki fungsi yang akan bertentangan dengan cmd/go yang akan ditambahkan? (Saya tidak mengatakan ini tidak memiliki jawaban, hanya saja lebih mudah untuk tidak harus menjawabnya.)

@josharian , harap pertimbangkan jenis pengecekan komentar di atas (https://github.com/golang/go/issues/35950#issuecomment-561443566).

@bradfitz ya, ini akan menjadi perubahan bahasa, dan akan membutuhkan dukungan go/types.

Sebenarnya, ada cara untuk melakukan ini tanpa harus mengubah bahasa—memerlukan static.go untuk memuat fungsi tanpa tubuh yang sama persis dengan apa yang akan diisi oleh cmd/go.

membutuhkan static.go untuk memuat fungsi tanpa tubuh yang sama persis dengan apa yang akan diisi oleh cmd/go.

Jika itu menghasilkan fungsi per file alih-alih menangkap semua embed.File() , itu akan memungkinkan kontrol ekspor per aset yang mudah.

Jadi seperti barang yang dihasilkan akan terlihat seperti:

EmbededFoo() embed.Asset {...}
embededBar() embed.Asset {...}

Sebuah posting blog yang saya tulis tentang file statis 4 bulan yang lalu. Lihat kalimat terakhir dalam kesimpulan :-)

@josharian

Anda dapat mengekspos fungsionalitas di my.pkg/postalcode dan meletakkan data di my.pkg/postalcode/data (atau my.pkg/postalcode/internal/data).

Itu — meski tidak elegan — bisa mengatasi kekhawatiran saya.

Bagaimana cara kerja kompatibilitas mundur?

Saya tidak melihat bagaimana masalah BC berlaku di sini. Bisakah Anda menguraikan?

Bagaimana Anda menandai paket data seperti itu?

Dengan pernyataan embed di go.mod ?

Mungkin saya tidak mengikuti apa yang Anda minta.

Tapi saya akan membalikkannya; bagaimana Anda menandai paket dengan hanya data seperti itu?

Apa yang Anda lakukan jika paket tersebut memiliki fungsi yang akan bertentangan dengan cmd/go yang akan ditambahkan?

  1. Menggunakan pendekatan yang diusulkan, saya tidak berpikir ini akan membutuhkan cmd/go untuk menambahkan fungsi apa pun.

  2. Bahkan jika cmd/go perlu menambahkan fungsi, saya membayangkan bahwa perilaku konflik dalam paket _existing_ akan menjadi _undefined_.

    Proposal mengasumsikan pengembang mengikuti prinsip tanggung jawab tunggal dan dengan demikian seharusnya hanya membangun paket dengan data menjadi paket data-sentris dan tidak menambahkan data ke paket logika-sentris yang ada.

    Tentu saja pengembang _could_ menambahkan ke paket yang ada dalam hal ini perilakunya tidak akan ditentukan. TKI, jika pengembang mengabaikan idiom, mereka akan berada di wilayah yang belum dipetakan.

Saya tidak mengatakan ini tidak memiliki jawaban, hanya saja lebih mudah untuk tidak harus menjawabnya.

Kecuali bahwa saya pikir jawabannya sederhana. Setidaknya untuk yang telah Anda berpose sejauh ini.

Saya pikir solusi apa pun yang menambahkan simbol atau nilai untuk simbol harus dalam cakupan paket, bukan cakupan modul. Karena unit kompilasi untuk Go adalah paket, bukan modul.

Jadi ini menghilangkan penggunaan go.mod untuk menentukan daftar file yang akan diimpor.

@dolmen jika hasil akhirnya dalam paketnya sendiri, maka ruang lingkup itu sendiri akan menjadi modul.

@urandom Tidak, cakupannya adalah paket yang mengimpor paket yang dihasilkan. Tapi bagaimanapun saya tidak berpikir bahwa paket yang dihasilkan penuh ada dalam cakupan proposal ini.

@urandom Tidak, cakupannya adalah paket yang mengimpor paket yang dihasilkan. Tapi bagaimanapun saya tidak berpikir bahwa paket yang dihasilkan penuh ada dalam cakupan proposal ini.

terlepas dari bagaimana proposal ini akan diimplementasikan, mengingat berbagai paket modul akan menggunakan hasil akhir, masuk akal bahwa definisi dari apa yang disematkan akan ditentukan pada tingkat modul. preseden untuk ini sudah ada juga, di ekosistem Java, di mana file yang disematkan dicakup modul dan ditambahkan dari direktori ajaib.

lebih jauh lagi, go.mod menyajikan cara terbersih untuk menambahkan fitur seperti itu (tidak ada komentar ajaib, atau direktori ajaib) tanpa merusak program yang ada.

Berikut adalah sesuatu yang belum terlihat disebutkan: API untuk alat pemrosesan sumber Go (kompiler, penganalisis statis) sama pentingnya dengan API runtime. Jenis API ini adalah nilai inti Go yang membantu menumbuhkan ekosistem (seperti go/ast / go/format dan go mod edit ).

API ini dapat digunakan oleh alat pra-prosesor (khususnya dalam langkah go:generate ) untuk mendapatkan daftar file yang akan disematkan atau dapat digunakan untuk menghasilkan referensi.

Dalam kasus paket khusus, saya tidak melihat perubahan apa pun dalam penguraian go.mod ( go mod alat) atau go/ast pengurai.

@dolmen

_"Saya pikir solusi apa pun yang menambahkan simbol atau nilai untuk simbol harus dicakup dalam paket, bukan dicakup modul. Karena unit kompilasi untuk Go adalah paket, bukan modul. Jadi, tidak ada penggunaan go.mod untuk menentukan daftar file untuk diimpor."_

Apa itu modul? Modul adalah _" kumpulan paket Go terkait yang diversi bersama sebagai satu unit ."_ Dengan demikian, sebuah modul dapat terdiri dari satu paket, dan satu paket dapat menjadi keseluruhan modul.

Dengan demikian, go.mod adalah tempat yang tepat untuk menentukan daftar file yang akan diimpor dengan asumsi tim Go mengadopsi go.mod melalui komentar khusus dan paket ajaib. Itu kecuali dan sampai tim Go memutuskan untuk menambahkan file go.pkg .

Selanjutnya, jika tim Go menerima go.mod sebagai tempat untuk menentukan file embed, maka siapa pun yang ingin menyematkan file harus menyediakan go.mod dengan instruksi embed , dan paket yang diwakili oleh direktori tempat file go.mod berada akan menjadi paket yang berisi file yang disematkan.

Tetapi jika bukan itu yang diinginkan pengembang, mereka harus membuat file go.mod dan meletakkannya di direktori untuk paket yang mereka inginkan untuk memuat file tertanam mereka.

Apakah ada skenario sah yang Anda bayangkan di mana kendala ini tidak akan bisa diterapkan?

@mikeschinkel , modul adalah kumpulan paket _related_. Namun, dimungkinkan (dan masuk akal!) untuk menggunakan satu paket dari modul tanpa menarik dependensi transitif (dan data!) dari paket lain di dalam modul itu.

File data umumnya dependensi per-paket, bukan per-modul, jadi informasi tentang cara menemukan dependensi tersebut harus ditempatkan bersama dengan paket — tidak disimpan sebagai metadata tingkat modul yang terpisah.

@bcmils

Tampaknya seseorang dapat mengganti 'File data' dalam pesan Anda dengan 'modul' dan itu akan tetap berlaku.
Sangat umum untuk memiliki modul khusus sebagai dependensi untuk paket spesifik Anda sendiri.
Namun kami menempatkan semuanya di dalam go.mod.

@urandom , tidak semua paket dalam modul yang ditunjukkan dalam file go.mod ditautkan ke biner akhir. (Menempatkan ketergantungan dalam file go.mod adalah _not_ sama dengan menautkan ketergantungan itu ke dalam program.)

Titik-meta

Jelas bahwa ini adalah sesuatu yang banyak orang pedulikan, dan komentar asli Brad di bagian atas bukanlah proposal yang sudah selesai daripada sketsa awal / titik awal / ajakan bertindak. Saya pikir akan masuk akal pada titik ini untuk mengakhiri diskusi khusus ini, minta Brad dan mungkin beberapa orang lain berkolaborasi pada dokumen desain terperinci, dan kemudian memulai masalah baru untuk diskusi tentang dokumen spesifik tersebut (belum ditulis) . Sepertinya itu akan membantu memfokuskan apa yang telah menjadi sedikit percakapan luas.

Pikiran?

Saya tidak yakin saya setuju dengan menutup yang ini, tetapi kita dapat Proposal-Tahan sampai ada dokumen desain dan kunci komentar sebentar. (Hampir semua komentar berlebihan pada saat ini, karena ada terlalu banyak komentar untuk dibaca orang untuk melihat apakah komentar mereka berlebihan...)

Mungkin kalau ada design doc maka yang ini bisa ditutup.

Atau tutup yang ini dan buka kembali #3035 (dengan komentar dibekukan) sehingga setidaknya satu edisi terbuka melacaknya.

Maaf melakukan ini tepat setelah berbicara tentang penutupan, tetapi diskusi dekat muncul tepat setelah komentar @bcmils dan sebelum saya dapat mengklarifikasi.

"_Namun, dimungkinkan (dan masuk akal!) untuk menggunakan satu paket dari modul tanpa menarik dependensi transitif (dan data!) dari paket lain di dalam modul itu."_

Ya, jelas itu _mungkin._ Tapi seperti praktik terbaik lainnya, praktik terbaik untuk paket data adalah membuat satu paket untuk sebuah modul, yang menyelesaikan masalah Anda. Jika itu berarti go.mod di root dan go.mod di subdirektori yang berisi paket data, biarlah.

Saya kira saya menganjurkan agar Anda tidak menjadikan sempurna menjadi musuh kebaikan di sini, di mana kebaikan di sini diidentifikasi sebagai go.mod menjadi tempat yang sempurna untuk menentukan file embed mengingat bahwa menurut sifatnya mereka adalah daftar komponen dari sebuah modul.

Maaf, tetapi paket adalah konsep dasar di Go, bukan modul.
Modul hanyalah sekelompok paket yang diversi sebagai satu unit.
Modul tidak memberikan kontribusi semantik tambahan di luar paket individu.
Itu bagian dari kesederhanaan mereka.
Apa pun yang kita lakukan di sini harus terikat pada paket, bukan modul.

Ke mana pun ini pergi dan bagaimanapun itu dilakukan, harus ada cara untuk mendapatkan daftar semua aset yang akan disematkan menggunakan daftar go (bukan hanya pola yang digunakan).

Menunda sampai Brad dan yang lainnya membuat dokumen desain formal.

Memblokir file tertentu seharusnya tidak terlalu sulit, terutama jika Anda menggunakan direktori static atau embed . Symlinks mungkin sedikit memperumitnya, tetapi Anda dapat mencegahnya menyematkan apa pun di luar modul saat ini atau, jika Anda menggunakan GOPATH, di luar paket yang berisi direktori.

Saya bukan penggemar komentar yang dikompilasi ke kode, tetapi saya juga menemukan paket semu yang memengaruhi kompilasi juga agak aneh. Jika pendekatan direktori tidak digunakan, mungkin lebih masuk akal untuk memiliki semacam deklarasi tingkat atas embed -benar dibangun ke dalam bahasa. Ini akan bekerja mirip dengan import , tetapi hanya akan mendukung jalur lokal dan memerlukan nama untuk ditugaskan. Sebagai contoh,

embed ui "./ui/build"

func main() {
  file, err := ui.Open("version.txt")
  if err != nil {
    panic(err)
  }
  version, err = ioutil.ReadAll(file)
  if err != nil {
    panic(err)
  }
  file.Close()

  log.Printf("UI Version: %s\n", bytes.TrimSpace(version))
  http.ListenAndServe(":8080", http.EmbeddedDir(ui))
}

Sunting: Anda mengalahkan saya, @jayconrod.

Ini bersih dan dapat dibaca namun saya tidak yakin tim go ingin memperkenalkan kata kunci baru

Ide yang saya ingat adalah hanya memiliki nama direktori khusus seperti "statis" yang berisi data statis dan secara otomatis membuatnya tersedia melalui API, tanpa perlu anotasi.

Menggunakan static sebagai nama direktori khusus agak membingungkan dan saya lebih suka menggunakan assets .
Gagasan lain yang tidak saya lihat di utas adalah mengizinkan mengimpor assets sebagai paket, misalnya import "example.com/internal/assets" . API yang terbuka masih membutuhkan desain, tetapi setidaknya terlihat lebih bersih daripada komentar khusus atau paket runtime/files -style baru.

Gagasan lain yang tidak saya lihat di utas adalah mengizinkan mengimpor aset sebagai paket

Ini diusulkan di sini: https://github.com/golang/go/issues/35950#issuecomment -562966654

Salah satu komplikasinya adalah untuk mengaktifkan typechecking, Anda perlu mengubah bahasa ini atau menyediakan fungsi tanpa tubuh untuk diisi oleh cmd/go .

Itu ide yang serupa, tetapi desain static.go memungkinkan mengubah jalur impor sewenang-wenang menjadi paket data, sementara direktori assets berfungsi lebih seperti testdata , internal atau vendor dalam hal menjadi "istimewa". Salah satu persyaratan yang mungkin dimiliki assets adalah tidak memuat paket Go (atau hanya mengizinkan dokumen), yaitu untuk kompatibilitas mundur implisit.

Ini juga dapat digabungkan dengan runtime/files -thingy API untuk mendapatkan file. Yaitu, menggunakan import s mentah untuk menyematkan pohon direktori dengan file dan kemudian menggunakan beberapa paket runtime untuk mengaksesnya. Bisa jadi os.Open , tapi itu tidak mungkin diterima.

shurcooL/vfsgen bersama shurcooL/httpgzip memiliki fitur bagus di mana konten dapat disajikan tanpa dekompresi.

misalnya

    rsp.Header().Set("Content-Type", "image/png")
    httpgzip.ServeContent(rsp, req, "", time.Time{}, file)

Fitur serupa sedang diusulkan untuk C++: std::embed :

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1040r0.html
https://mobile.twitter.com/Cor3ntin/status/1208389050698215427

Ini mungkin berguna sebagai inspirasi untuk desain, dan untuk mengumpulkan kemungkinan kasus penggunaan.

Aku agak terlambat ke pesta, tapi aku punya ide. Alih-alih komentar khusus, direktori khusus tetap (statis), atau pendekatan yang tampaknya tidak boleh untuk memperluas go.mod: Bagaimana dengan file manifes baru yang per paket: go.res

  • Berisi daftar file. Tidak ada jalur atau gumpalan, hanya nama di direktori paket saat ini. Hasilkan dari gumpalan sebelum melakukan jika Anda perlu.

    • __Edit__ mungkin memerlukan satu baris package mypackagename di bagian atas, seperti yang dimiliki file go. Atau, Anda dapat menyertakan nama paket dalam nama file (mis. namapaketsaya.go.res). Secara pribadi, saya lebih suka baris header package .

  • Paket inti baru yang disebut "sumber daya" atau mungkin "io/sumber daya". Memiliki setidaknya satu fungsi: func Read(name string) (io.Reader, bool) untuk membaca sumber daya yang disematkan dalam paket saat ini.

    • __Edit__ Tidak yakin paket inti bekerja dengan cara ini. Mungkin harus berupa fungsi pribadi paket yang dihasilkan (misalnya func readresource(name string) (io.Reader, bool) )

  • Jika Anda menginginkan sumber daya dalam sub direktori, maka buatlah subdirektori tersebut sebagai paket dengan menambahkan file go.res dan setidaknya satu file .go . File go mengekspor API publik Anda sendiri untuk mengakses sumber daya dalam paket subdirektori. File go dan API yang diekspor diperlukan, karena sumber daya dari paket lain tidak diekspor secara otomatis (berdasarkan desain). Anda juga dapat menyesuaikan bagaimana mereka diekspor dengan cara ini.

    • __Edit__ sebagai alternatif, jika Anda memerlukan struktur direktori dan/atau kompresi, gunakan sumber daya tar. Ini memungkinkan hal-hal seperti bundel webpack, yang sudah memerlukan kompilasi (dan mungkin mendapat manfaat dari pra-kompresi). Membawa mereka satu langkah lebih jauh ke tar itu sederhana.

  • __Edit__ Butuh manifes? Cukup sertakan file go.res itu sendiri sebagai sumber daya. Kami bahkan tidak perlu membuat fungsi listresources.

Sangat sederhana. Satu fungsi baru. Satu berkas baru. Tidak ada jalan. Tidak ada kompresi. Tidak ada sintaks baru. Tidak ada sihir. Dapat diperluas. Akses hanya baca melalui pembaca (tetapi terbuka untuk pola akses alternatif di masa mendatang). Hampir nol kemungkinan melanggar paket yang ada. Tetap dengan paket sebagai konstruk inti saat berjalan.

__Edit__ Setelah pencarian github language:go filename:go.res extension:res , sepertinya go.res akan menjadi nama file yang cukup aman untuk digunakan. Tidak ada kecocokan di repo go, dan hanya sedikit di repo non-go.

Saya suka ide @chris.ackermanm. Tapi saya lebih suka kombinasi:

File go.res yang menentukan namespace dalam direktori.

Hal ini memungkinkan untuk

  • beberapa termasuk selama namespace berbeda
  • tidak mengetahui file sebelumnya dan harus membuat daftar

Yang terakhir harus menangani output webpack dan sejenisnya yang dapat mengubah tata letak karena pembaruan, opsi berbeda, apa pun yang dapat Anda pikirkan.

Mengenai kompresi: yang menurut saya lebih merupakan fitur dalam hal tidak meledaknya ukuran biner dan harus transparan terhadap kode penggunaan.

Nanti Anda dapat mengizinkan penulisan ulang seperti

filename => stored-as.png

Hanya 2 saya

@sascha-andres Sepertinya sangat sederhana dan tidak ada keajaiban adalah nada dari utas ini. Lihat suntingan yang saya buat untuk komentar saya tentang saran Anda.

Saya tidak suka pemetaan. Tidak dibutuhkan. Itu mungkin dengan mengekspos fungsi baca Anda sendiri dari paket terpisah, dan sekarang kita membutuhkan sintaks file baru, atau sesuatu yang lebih kompleks daripada file-per-line.

Hai

Usulan ini luar biasa!

Dan saya memiliki pendekatan saya untuk menyematkan aset. tidak perlu memperkenalkan alat apa pun selain GNU bintools. Ini agak kotor, tetapi bekerja dengan baik untuk saya untuk saat ini. Saya hanya ingin membagikannya dan melihat apakah itu membantu.

pendekatan saya adalah dengan hanya menyematkan aset saya (dikompresi dengan tar&gz) di bagian elf/pe32 dengan objcopy, dan membacanya melalui paket debug/elf dan debug/pe32 bersama dengan Zip bila diperlukan. semua yang perlu saya ingat adalah untuk tidak menyentuh bagian yang ada. semua aset tidak dapat diubah dan kemudian kode membaca konten dan memprosesnya dalam memori.

saya cukup berpengalaman dalam desain bahasa atau desain kompiler. jadi saya hanya akan menggunakan pendekatan yang dijelaskan di atas dan menggunakan .goassets atau sesuatu seperti itu sebagai nama bagian. dan membuat kompresi opsional.

pendekatan saya adalah dengan hanya menyematkan aset saya (dikompresi dengan tar&gz) di bagian elf/pe32 dengan objcopy, dan membacanya melalui paket debug/elf dan debug/pe32 bersama dengan Zip bila diperlukan. semua yang perlu saya ingat adalah untuk tidak menyentuh bagian yang ada. semua aset tidak dapat diubah dan kemudian kode membaca konten dan memprosesnya dalam memori.

Kedengarannya seperti itu bekerja pada elf / pe32 tapi bagaimana dengan mach-o / plan9 ?

Masalah lainnya adalah ia bergantung pada pembukaan pegangan file pada yang dapat dieksekusi, jika yang dapat dieksekusi telah ditimpa/diperbarui/dihapus maka ini akan mengembalikan data yang berbeda, tidak yakin apakah itu masalah yang sah atau fitur yang tidak terduga.

Saya sendiri sedikit mencoba (menggunakan debug/macho ), tetapi saya tidak dapat melihat cara untuk menjalankan lintas platform ini, saya sedang membangun di macOS dan GNU binutils yang diinstal sepertinya merusak mach-o-x86-64 file (itu bisa jadi kurangnya pemahaman struktur mach-o dan terlalu lama sejak saya melihat objcopy ).

Masalah lainnya adalah ia bergantung pada pembukaan pegangan file pada yang dapat dieksekusi

Saya cukup yakin bahwa pemuat program akan (atau dapat) memuat bagian sumber daya ke dalam memori, jadi tidak perlu menggunakan paket debug. Meskipun mengakses data akan membutuhkan lebih banyak mengutak-atik file objek daripada nilainya.

Mengapa tidak mengikuti apa yang berhasil -- misalnya bagaimana Java melakukannya. Saya akan membutuhkan hal-hal yang menjadi go-ish besar, tetapi sesuatu di baris:

  • buat file go.res atau ubah go.mod untuk menunjuk ke direktori tempat sumber daya berada
  • semua file dari direktori ini secara otomatis disertakan, tidak ada pengecualian oleh kompiler di executable akhir
  • bahasa menyediakan API seperti jalur untuk mengakses sumber daya ini

Kompresi, dll. harus berada di luar cakupan pengelompokan sumber daya ini dan hingga skrip // go:generate jika diperlukan.

Ada yang pernah lihat markbates/pkger ? Ini adalah solusi yang cukup sederhana untuk menggunakan go.mod sebagai direktori kerja saat ini. Dengan asumsi index.html akan disematkan, membukanya akan menjadi pkger.Open("/index.html") . Saya pikir ini adalah ide yang lebih baik daripada hardcoding direktori static/ dalam proyek.

Perlu juga disebutkan bahwa Go tidak memiliki persyaratan struktur yang signifikan untuk sebuah proyek sejauh yang saya bisa lihat. go.mod hanyalah sebuah file dan tidak banyak orang yang pernah menggunakan vendor/ . Saya pribadi tidak berpikir bahwa direktori static/ akan ada gunanya.

Karena kita sudah memiliki cara untuk memasukkan data (walaupun terbatas) ke dalam build melalui tanda tautan ldflags ada -X importpath.name=value , dapatkah jalur kode itu disesuaikan untuk menerima -X importpath.name=@filename untuk menyuntikkan data arbitrer eksternal?

Saya menyadari ini tidak mencakup semua tujuan yang dinyatakan dari masalah asli, tetapi sebagai perpanjangan dari fungsi -X apakah ini merupakan langkah maju yang masuk akal?

(Dan jika itu berhasil, maka memperluas sintaks go.mod sebagai cara yang lebih rapi untuk menentukan nilai ldflags -X adalah langkah wajar berikutnya?)

Itu ide yang sangat menarik, tapi saya khawatir tentang implikasi keamanannya.

Sangat umum untuk melakukan -X 'pkg.BuildVersion=$(git rev-parse HEAD)' , tetapi kita tidak ingin membiarkan go.mod menjalankan perintah sewenang-wenang, bukan? (Saya kira go generate tidak, tapi itu bukan sesuatu yang biasanya Anda jalankan untuk paket OSS yang diunduh.) Jika go.mod tidak dapat menanganinya, itu akan kehilangan kasus penggunaan utama, jadi ldflags masih sangat umum.

Lalu ada masalah lain untuk memastikan @filename bukan symlink ke /etc/passwd atau apa pun.

Menggunakan tautan menghalangi dukungan untuk WASM, dan mungkin target lain yang tidak menggunakan tautan.

Berdasarkan diskusi di sini, @bradfitz dan saya membuat desain yang berada di tengah-tengah dari dua pendekatan yang dipertimbangkan di atas, mengambil apa yang tampaknya menjadi yang terbaik dari masing-masing pendekatan. Saya telah memposting draf dokumen desain, video, dan kode (tautan di bawah). Alih-alih mengomentari masalah ini, silakan gunakan T&J Reddit untuk komentar tentang desain draf khusus ini - Utas dan skala Reddit diskusi lebih baik daripada GitHub. Terima kasih!

Video: https://golang.org/s/draft-embed-video
Desain: https://golang.org/s/draft-embed-design
T&J: https://golang.org/s/draft-embed-reddit
Kode: https://golang.org/s/draft-embed-code

@rsc Menurut pendapat saya, proposal go:embed lebih rendah daripada menyediakan eksekusi kode Go _universal_ sandboxed pada waktu kompilasi yang akan mencakup membaca file dan mengubah data baca menjadi _format optimal_ paling cocok untuk dikonsumsi saat runtime.

@atomsymbol Kedengarannya seperti sesuatu di luar cakupan masalah ini.

@atomsymbol Kedengarannya seperti sesuatu di luar cakupan masalah ini.

Saya sadar akan hal itu.

Saya membaca proposal dan memindai kode, tetapi tidak dapat menemukan jawaban untuk ini: Apakah skema penyematan ini berisi informasi tentang file pada disk (~os.Stat)? Atau akankah stempel waktu ini disetel ulang untuk membangun waktu? Either way, ini adalah informasi potongan berguna yang digunakan di berbagai tempat, misalnya kami dapat mengirim 304 untuk aset yang tidak berubah berdasarkan ini.

Terima kasih!

Sunting: menemukannya di utas reddit.

Waktu modifikasi untuk semua file yang disematkan adalah waktu nol, persis untuk masalah reproduktifitas yang Anda daftarkan. (Modul bahkan tidak mencatat waktu modifikasi, lagi-lagi untuk alasan yang sama.)

https://old.reddit.com/r/golang/comments/hv96ny/qa_goembed_draft_design/fytj7my/

Either way, ini adalah informasi potongan berguna yang digunakan di berbagai tempat, misalnya kami dapat mengirim 304 untuk aset yang tidak berubah berdasarkan ini.

Header ETag berdasarkan hash data file akan menyelesaikan masalah itu tanpa harus tahu apa-apa tentang tanggal. Tapi itu harus diketahui oleh http.HandlerFS atau sesuatu untuk dapat bekerja dan tidak menyia-nyiakan sumber daya itu harus dilakukan hanya sekali per file.

Tapi itu harus diketahui oleh http.HandlerFS atau sesuatu untuk dapat bekerja dan tidak menyia-nyiakan sumber daya itu harus dilakukan hanya sekali per file.

Bagaimana http.HandlerFS mengetahui bahwa fs.FS tidak dapat diubah? Haruskah ada antarmuka opsional IsImmutable() bool ?

Bagaimana http.HandlerFS mengetahui bahwa fs.FS tidak dapat diubah? Haruskah ada antarmuka opsional IsImmutable() bool ?

Saya tidak ingin masuk ke detail implementasi karena saya bukan perancang hal-hal ini tetapi http.HandlerFS dapat memeriksa apakah itu jenis embed.FS dan bertindak berdasarkan itu sebagai kasus khusus, saya rasa tidak ada yang mau perluas FS API sekarang. Mungkin juga ada argumen opsi untuk HandlerFS secara khusus untuk memintanya memperlakukan sistem file sebagai tidak dapat diubah. Juga jika ini dilakukan pada permulaan aplikasi dan semua ctime/mtime memiliki nilai nol handlerFS dapat menggunakan info itu untuk "mengetahui" bahwa file tersebut tidak berubah tetapi ada juga sistem file yang mungkin tidak memiliki mtime atau menonaktifkannya sehingga ada mungkin ada masalah di sana juga.

Saya tidak melihat komentar tentang masalah ini.

@atomsymbol selamat datang kembali! Senang melihat Anda berkomentar di sini lagi.
Saya setuju pada prinsipnya bahwa jika kita memiliki sandboxing banyak hal akan lebih mudah.
Di sisi lain, banyak hal mungkin lebih sulit - pembangunan mungkin tidak akan pernah selesai.
Bagaimanapun, kita pasti tidak memiliki sandbox seperti itu hari ini. :-)

@kokes Saya tidak yakin tentang detailnya,
tapi kami akan memastikan penyajian embed.File melalui HTTP membuat ETag benar secara default.

Saya telah mengajukan #41191 untuk menerima rancangan desain yang diposting kembali pada bulan Juli.
Saya akan menutup masalah ini sebagai digantikan oleh yang itu.
Terima kasih atas diskusi awal yang hebat di sini.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat