all: support `$embed_file('embed.vv', .zlib)` (#12654)
parent
0f50ac3260
commit
ace63594bf
|
@ -0,0 +1,44 @@
|
||||||
|
module main
|
||||||
|
|
||||||
|
import compress.zlib
|
||||||
|
import os
|
||||||
|
|
||||||
|
enum CompressionType {
|
||||||
|
zlib
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if os.args.len != 5 {
|
||||||
|
eprintln('v compress <type> <in> <out>')
|
||||||
|
eprintln('supported types: zlib')
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
compression_type := match os.args[2] {
|
||||||
|
'zlib' {
|
||||||
|
CompressionType.zlib
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
eprintln('unsupported type: ${os.args[1]}')
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
path := os.args[3]
|
||||||
|
content := os.read_bytes(path) or {
|
||||||
|
eprintln('unable to read "$path": $err')
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
compressed := match compression_type {
|
||||||
|
.zlib {
|
||||||
|
zlib.compress(content) or {
|
||||||
|
eprintln('compression error: $err')
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out_path := os.args[4]
|
||||||
|
|
||||||
|
os.write_file_array(out_path, compressed) or {
|
||||||
|
eprintln('failed to write "$out_path": $err')
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ const (
|
||||||
'build-vbinaries',
|
'build-vbinaries',
|
||||||
'check-md',
|
'check-md',
|
||||||
'complete',
|
'complete',
|
||||||
|
'compress',
|
||||||
'doc',
|
'doc',
|
||||||
'doctor',
|
'doctor',
|
||||||
'fmt',
|
'fmt',
|
||||||
|
|
11
doc/docs.md
11
doc/docs.md
|
@ -4982,6 +4982,17 @@ executable, increasing your binary size, but making it more self contained
|
||||||
and thus easier to distribute. In this case, `embedded_file.data()` will cause *no IO*,
|
and thus easier to distribute. In this case, `embedded_file.data()` will cause *no IO*,
|
||||||
and it will always return the same data.
|
and it will always return the same data.
|
||||||
|
|
||||||
|
`$embed_file` supports compression of the embedded file when compiling with `-prod`.
|
||||||
|
Currently only one compression type is supported: `zlib`
|
||||||
|
|
||||||
|
```v ignore
|
||||||
|
import os
|
||||||
|
fn main() {
|
||||||
|
embedded_file := $embed_file('v.png', .zlib) // compressed using zlib
|
||||||
|
os.write_file('exported.png', embedded_file.to_string()) ?
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
#### `$tmpl` for embedding and parsing V template files
|
#### `$tmpl` for embedding and parsing V template files
|
||||||
|
|
||||||
V has a simple template language for text and html templates, and they can easily
|
V has a simple template language for text and html templates, and they can easily
|
||||||
|
|
|
@ -648,6 +648,12 @@ pub struct EmbeddedFile {
|
||||||
pub:
|
pub:
|
||||||
rpath string // used in the source code, as an ID/key to the embed
|
rpath string // used in the source code, as an ID/key to the embed
|
||||||
apath string // absolute path during compilation to the resource
|
apath string // absolute path during compilation to the resource
|
||||||
|
compression_type string
|
||||||
|
pub mut:
|
||||||
|
// these are set by gen_embed_file_init in v/gen/c/embed
|
||||||
|
is_compressed bool
|
||||||
|
bytes []byte
|
||||||
|
len int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each V source file is represented by one File structure.
|
// Each V source file is represented by one File structure.
|
||||||
|
@ -1543,7 +1549,6 @@ pub:
|
||||||
vweb_tmpl File
|
vweb_tmpl File
|
||||||
//
|
//
|
||||||
is_embed bool
|
is_embed bool
|
||||||
embed_file EmbeddedFile
|
|
||||||
//
|
//
|
||||||
is_env bool
|
is_env bool
|
||||||
env_pos token.Position
|
env_pos token.Position
|
||||||
|
@ -1554,6 +1559,7 @@ pub mut:
|
||||||
result_type Type
|
result_type Type
|
||||||
env_value string
|
env_value string
|
||||||
args []CallArg
|
args []CallArg
|
||||||
|
embed_file EmbeddedFile
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct None {
|
pub struct None {
|
||||||
|
|
|
@ -23,6 +23,7 @@ const (
|
||||||
valid_comptime_if_os = ['windows', 'ios', 'macos', 'mach', 'darwin', 'hpux', 'gnu',
|
valid_comptime_if_os = ['windows', 'ios', 'macos', 'mach', 'darwin', 'hpux', 'gnu',
|
||||||
'qnx', 'linux', 'freebsd', 'openbsd', 'netbsd', 'bsd', 'dragonfly', 'android', 'solaris',
|
'qnx', 'linux', 'freebsd', 'openbsd', 'netbsd', 'bsd', 'dragonfly', 'android', 'solaris',
|
||||||
'haiku', 'serenity', 'vinix']
|
'haiku', 'serenity', 'vinix']
|
||||||
|
valid_comptime_compression_types = ['none', 'zlib']
|
||||||
valid_comptime_if_compilers = ['gcc', 'tinyc', 'clang', 'mingw', 'msvc', 'cplusplus']
|
valid_comptime_if_compilers = ['gcc', 'tinyc', 'clang', 'mingw', 'msvc', 'cplusplus']
|
||||||
valid_comptime_if_platforms = ['amd64', 'i386', 'aarch64', 'arm64', 'arm32', 'rv64', 'rv32']
|
valid_comptime_if_platforms = ['amd64', 'i386', 'aarch64', 'arm64', 'arm32', 'rv64', 'rv32']
|
||||||
valid_comptime_if_cpu_features = ['x64', 'x32', 'little_endian', 'big_endian']
|
valid_comptime_if_cpu_features = ['x64', 'x32', 'little_endian', 'big_endian']
|
||||||
|
@ -5866,7 +5867,12 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
|
||||||
return ast.string_type
|
return ast.string_type
|
||||||
}
|
}
|
||||||
if node.is_embed {
|
if node.is_embed {
|
||||||
c.file.embedded_files << node.embed_file
|
// c.file.embedded_files << node.embed_file
|
||||||
|
if node.embed_file.compression_type !in checker.valid_comptime_compression_types {
|
||||||
|
supported := checker.valid_comptime_compression_types.map('.$it').join(', ')
|
||||||
|
c.error('not supported compression type: .${node.embed_file.compression_type}. supported: $supported',
|
||||||
|
node.pos)
|
||||||
|
}
|
||||||
return c.table.find_type_idx('v.embed_file.EmbedFileData')
|
return c.table.find_type_idx('v.embed_file.EmbedFileData')
|
||||||
}
|
}
|
||||||
if node.is_vweb {
|
if node.is_vweb {
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
vlib/v/checker/tests/embed_unknown_compress_type.vv:3:10: error: not supported compression type: .this_is_not_a_valid_compression_type. supported: .none, .zlib
|
||||||
|
1 |
|
||||||
|
2 | fn main() {
|
||||||
|
3 | test := $embed_file('embed_unknown_compress_type.vv', .this_is_not_a_valid_compression_type)
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
4 | println(test.to_string())
|
||||||
|
5 | }
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test := $embed_file('embed_unknown_compress_type.vv', .this_is_not_a_valid_compression_type)
|
||||||
|
println(test.to_string())
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
[has_globals]
|
||||||
|
module embed_file
|
||||||
|
|
||||||
|
interface Decoder {
|
||||||
|
decompress([]byte) ?[]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EmbedFileDecoders {
|
||||||
|
mut:
|
||||||
|
decoders map[string]Decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
__global g_embed_file_decoders = &EmbedFileDecoders{}
|
||||||
|
|
||||||
|
pub fn register_decoder(compression_type string, decoder Decoder) {
|
||||||
|
g_embed_file_decoders.decoders[compression_type] = decoder
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ pub const (
|
||||||
// EmbedFileData encapsulates functionality for the `$embed_file()` compile time call.
|
// EmbedFileData encapsulates functionality for the `$embed_file()` compile time call.
|
||||||
pub struct EmbedFileData {
|
pub struct EmbedFileData {
|
||||||
apath string
|
apath string
|
||||||
|
compression_type string
|
||||||
mut:
|
mut:
|
||||||
compressed &byte
|
compressed &byte
|
||||||
uncompressed &byte
|
uncompressed &byte
|
||||||
|
@ -29,6 +30,7 @@ pub fn (mut ed EmbedFileData) free() {
|
||||||
unsafe {
|
unsafe {
|
||||||
ed.path.free()
|
ed.path.free()
|
||||||
ed.apath.free()
|
ed.apath.free()
|
||||||
|
ed.compression_type.free()
|
||||||
if ed.free_compressed {
|
if ed.free_compressed {
|
||||||
free(ed.compressed)
|
free(ed.compressed)
|
||||||
ed.compressed = &byte(0)
|
ed.compressed = &byte(0)
|
||||||
|
@ -59,11 +61,18 @@ pub fn (original &EmbedFileData) to_bytes() []byte {
|
||||||
pub fn (mut ed EmbedFileData) data() &byte {
|
pub fn (mut ed EmbedFileData) data() &byte {
|
||||||
if !isnil(ed.uncompressed) {
|
if !isnil(ed.uncompressed) {
|
||||||
return ed.uncompressed
|
return ed.uncompressed
|
||||||
} else {
|
}
|
||||||
if isnil(ed.uncompressed) && !isnil(ed.compressed) {
|
if isnil(ed.uncompressed) && !isnil(ed.compressed) {
|
||||||
// TODO implement uncompression
|
decoder := g_embed_file_decoders.decoders[ed.compression_type] or {
|
||||||
// See also C Gen.gen_embedded_data() where the compression should occur.
|
panic('EmbedFileData error: unknown compression of "$ed.path": "$ed.compression_type"')
|
||||||
ed.uncompressed = ed.compressed
|
}
|
||||||
|
compressed := unsafe { ed.compressed.vbytes(ed.len) }
|
||||||
|
decompressed := decoder.decompress(compressed) or {
|
||||||
|
panic('EmbedFileData error: decompression of "$ed.path" failed: $err')
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
ed.uncompressed = &byte(memdup(decompressed.data, ed.len))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
mut path := os.resource_abs_path(ed.path)
|
mut path := os.resource_abs_path(ed.path)
|
||||||
if !os.is_file(path) {
|
if !os.is_file(path) {
|
||||||
|
@ -78,7 +87,6 @@ pub fn (mut ed EmbedFileData) data() &byte {
|
||||||
ed.uncompressed = bytes.data
|
ed.uncompressed = bytes.data
|
||||||
ed.free_uncompressed = true
|
ed.free_uncompressed = true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return ed.uncompressed
|
return ed.uncompressed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,19 +99,20 @@ pub fn (mut ed EmbedFileData) data() &byte {
|
||||||
pub struct EmbedFileIndexEntry {
|
pub struct EmbedFileIndexEntry {
|
||||||
id int
|
id int
|
||||||
path string
|
path string
|
||||||
|
algo string
|
||||||
data &byte
|
data &byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// find_index_entry_by_path is used internally by the V compiler:
|
// find_index_entry_by_path is used internally by the V compiler:
|
||||||
pub fn find_index_entry_by_path(start voidptr, path string) &EmbedFileIndexEntry {
|
pub fn find_index_entry_by_path(start voidptr, path string, algo string) &EmbedFileIndexEntry {
|
||||||
mut x := &EmbedFileIndexEntry(start)
|
mut x := &EmbedFileIndexEntry(start)
|
||||||
for !(x.path == path || isnil(x.data)) {
|
for x.id >= 0 && x.data != 0 && (x.algo != algo || x.path != path) {
|
||||||
unsafe {
|
unsafe {
|
||||||
x++
|
x++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$if debug_embed_file_in_prod ? {
|
$if debug_embed_file_in_prod ? {
|
||||||
eprintln('>> v.embed_file find_index_entry_by_path ${ptr_str(start)}, path: "$path" => ${ptr_str(x)}')
|
eprintln('>> v.embed_file find_index_entry_by_path ${ptr_str(start)}, id: $x.id, path: "$path", algo: "$algo" => ${ptr_str(x)}')
|
||||||
}
|
}
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
|
@ -3828,7 +3828,7 @@ fn (mut g Gen) expr(node ast.Expr) {
|
||||||
}
|
}
|
||||||
ast.Comment {}
|
ast.Comment {}
|
||||||
ast.ComptimeCall {
|
ast.ComptimeCall {
|
||||||
g.comptime_call(node)
|
g.comptime_call(mut node)
|
||||||
}
|
}
|
||||||
ast.ComptimeSelector {
|
ast.ComptimeSelector {
|
||||||
g.comptime_selector(node)
|
g.comptime_selector(node)
|
||||||
|
|
|
@ -28,10 +28,10 @@ fn (mut g Gen) comptime_selector(node ast.ComptimeSelector) {
|
||||||
g.expr(node.field_expr)
|
g.expr(node.field_expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) comptime_call(node ast.ComptimeCall) {
|
fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) {
|
||||||
if node.is_embed {
|
if node.is_embed {
|
||||||
// $embed_file('/path/to/file')
|
// $embed_file('/path/to/file')
|
||||||
g.gen_embed_file_init(node)
|
g.gen_embed_file_init(mut node)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if node.method_name == 'env' {
|
if node.method_name == 'env' {
|
||||||
|
|
|
@ -12,6 +12,8 @@ const testdata_folder = os.join_path(vroot, 'vlib', 'v', 'gen', 'c', 'testdata')
|
||||||
|
|
||||||
const diff_cmd = diff.find_working_diff_command() or { '' }
|
const diff_cmd = diff.find_working_diff_command() or { '' }
|
||||||
|
|
||||||
|
const show_compilation_output = os.getenv('VTEST_SHOW_COMPILATION_OUTPUT').int() == 1
|
||||||
|
|
||||||
fn mm(s string) string {
|
fn mm(s string) string {
|
||||||
return term.colorize(term.magenta, s)
|
return term.colorize(term.magenta, s)
|
||||||
}
|
}
|
||||||
|
@ -34,9 +36,13 @@ fn test_out_files() ? {
|
||||||
mut total_errors := 0
|
mut total_errors := 0
|
||||||
for out_path in paths {
|
for out_path in paths {
|
||||||
basename, path, relpath, out_relpath := target2paths(out_path, '.out')
|
basename, path, relpath, out_relpath := target2paths(out_path, '.out')
|
||||||
print(mm('v run $relpath') + ' == ${mm(out_relpath)} ')
|
|
||||||
pexe := os.join_path(output_path, '${basename}.exe')
|
pexe := os.join_path(output_path, '${basename}.exe')
|
||||||
compilation := os.execute('"$vexe" -o "$pexe" "$path"')
|
//
|
||||||
|
file_options := get_file_options(path)
|
||||||
|
alloptions := '-o "$pexe" $file_options.vflags'
|
||||||
|
print(mm('v $alloptions run $relpath') + ' == ${mm(out_relpath)} ')
|
||||||
|
//
|
||||||
|
compilation := os.execute('"$vexe" $alloptions "$path"')
|
||||||
ensure_compilation_succeeded(compilation)
|
ensure_compilation_succeeded(compilation)
|
||||||
res := os.execute(pexe)
|
res := os.execute(pexe)
|
||||||
if res.exit_code < 0 {
|
if res.exit_code < 0 {
|
||||||
|
@ -99,12 +105,14 @@ fn test_c_must_have_files() ? {
|
||||||
}
|
}
|
||||||
paths := vtest.filter_vtest_only(tests, basepath: testdata_folder)
|
paths := vtest.filter_vtest_only(tests, basepath: testdata_folder)
|
||||||
mut total_errors := 0
|
mut total_errors := 0
|
||||||
|
mut failed_descriptions := []string{cap: paths.len}
|
||||||
for must_have_path in paths {
|
for must_have_path in paths {
|
||||||
basename, path, relpath, must_have_relpath := target2paths(must_have_path, '.c.must_have')
|
basename, path, relpath, must_have_relpath := target2paths(must_have_path, '.c.must_have')
|
||||||
file_options := get_file_options(path)
|
file_options := get_file_options(path)
|
||||||
alloptions := '-o - $file_options.vflags'
|
alloptions := '-o - $file_options.vflags'
|
||||||
print(mm('v $alloptions $relpath') +
|
description := mm('v $alloptions $relpath') +
|
||||||
' matches all line patterns in ${mm(must_have_relpath)} ')
|
' matches all line patterns in ${mm(must_have_relpath)} '
|
||||||
|
print(description)
|
||||||
cmd := '$vexe $alloptions $path'
|
cmd := '$vexe $alloptions $path'
|
||||||
compilation := os.execute(cmd)
|
compilation := os.execute(cmd)
|
||||||
ensure_compilation_succeeded(compilation)
|
ensure_compilation_succeeded(compilation)
|
||||||
|
@ -122,6 +130,9 @@ fn test_c_must_have_files() ? {
|
||||||
eprintln('$must_have_path:${idx_expected_line + 1}: expected match error:')
|
eprintln('$must_have_path:${idx_expected_line + 1}: expected match error:')
|
||||||
eprintln('`$cmd` did NOT produce expected line:')
|
eprintln('`$cmd` did NOT produce expected line:')
|
||||||
eprintln(term.colorize(term.red, eline))
|
eprintln(term.colorize(term.red, eline))
|
||||||
|
if description !in failed_descriptions {
|
||||||
|
failed_descriptions << description
|
||||||
|
}
|
||||||
total_errors++
|
total_errors++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -129,8 +140,10 @@ fn test_c_must_have_files() ? {
|
||||||
if nmatches == expected_lines.len {
|
if nmatches == expected_lines.len {
|
||||||
println(term.green('OK'))
|
println(term.green('OK'))
|
||||||
} else {
|
} else {
|
||||||
|
if show_compilation_output {
|
||||||
eprintln('> ALL lines:')
|
eprintln('> ALL lines:')
|
||||||
eprintln(compilation.output)
|
eprintln(compilation.output)
|
||||||
|
}
|
||||||
eprintln('--------- failed patterns: -------------------------------------------')
|
eprintln('--------- failed patterns: -------------------------------------------')
|
||||||
for fpattern in failed_patterns {
|
for fpattern in failed_patterns {
|
||||||
eprintln(fpattern)
|
eprintln(fpattern)
|
||||||
|
@ -138,6 +151,13 @@ fn test_c_must_have_files() ? {
|
||||||
eprintln('----------------------------------------------------------------------')
|
eprintln('----------------------------------------------------------------------')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if failed_descriptions.len > 0 {
|
||||||
|
eprintln('--------- failed commands: -------------------------------------------')
|
||||||
|
for fd in failed_descriptions {
|
||||||
|
eprintln(' > $fd')
|
||||||
|
}
|
||||||
|
eprintln('----------------------------------------------------------------------')
|
||||||
|
}
|
||||||
assert total_errors == 0
|
assert total_errors == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
module c
|
module c
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import rand
|
||||||
import v.ast
|
import v.ast
|
||||||
|
import v.pref
|
||||||
|
|
||||||
fn (mut g Gen) embed_file_is_prod_mode() bool {
|
fn (mut g Gen) embed_file_is_prod_mode() bool {
|
||||||
if g.pref.is_prod || 'debug_embed_file_in_prod' in g.pref.compile_defines {
|
if g.pref.is_prod || 'debug_embed_file_in_prod' in g.pref.compile_defines {
|
||||||
|
@ -11,7 +13,51 @@ fn (mut g Gen) embed_file_is_prod_mode() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// gen_embed_file_struct generates C code for `$embed_file('...')` calls.
|
// gen_embed_file_struct generates C code for `$embed_file('...')` calls.
|
||||||
fn (mut g Gen) gen_embed_file_init(node ast.ComptimeCall) {
|
fn (mut g Gen) gen_embed_file_init(mut node ast.ComptimeCall) {
|
||||||
|
if g.embed_file_is_prod_mode() {
|
||||||
|
file_bytes := os.read_bytes(node.embed_file.apath) or {
|
||||||
|
panic('unable to read file: "$node.embed_file.rpath')
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.embed_file.compression_type == 'none' {
|
||||||
|
node.embed_file.bytes = file_bytes
|
||||||
|
} else {
|
||||||
|
cache_dir := os.join_path(os.vmodules_dir(), 'cache', 'embed_file')
|
||||||
|
cache_key := rand.ulid()
|
||||||
|
// cache_key := md5.hexhash(node.embed_file.apath)
|
||||||
|
if !os.exists(cache_dir) {
|
||||||
|
os.mkdir_all(cache_dir) or { panic(err) }
|
||||||
|
}
|
||||||
|
cache_path := os.join_path(cache_dir, cache_key)
|
||||||
|
|
||||||
|
vexe := pref.vexe_path()
|
||||||
|
result := os.execute('"$vexe" compress $node.embed_file.compression_type "$node.embed_file.apath" "$cache_path"')
|
||||||
|
if result.exit_code != 0 {
|
||||||
|
eprintln('unable to compress file "$node.embed_file.rpath": $result.output')
|
||||||
|
node.embed_file.bytes = file_bytes
|
||||||
|
} else {
|
||||||
|
compressed_bytes := os.read_bytes(cache_path) or {
|
||||||
|
eprintln('unable to read compressed file')
|
||||||
|
{
|
||||||
|
}
|
||||||
|
[]byte{}
|
||||||
|
}
|
||||||
|
os.rm(cache_path) or {} // clean up
|
||||||
|
node.embed_file.is_compressed = compressed_bytes.len > 0
|
||||||
|
&& compressed_bytes.len < file_bytes.len
|
||||||
|
node.embed_file.bytes = if node.embed_file.is_compressed {
|
||||||
|
compressed_bytes
|
||||||
|
} else {
|
||||||
|
file_bytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.embed_file.bytes.len > 5242880 {
|
||||||
|
eprintln('embedding of files >= ~5MB is currently not well supported')
|
||||||
|
}
|
||||||
|
node.embed_file.len = file_bytes.len
|
||||||
|
}
|
||||||
|
|
||||||
g.writeln('(v__embed_file__EmbedFileData){')
|
g.writeln('(v__embed_file__EmbedFileData){')
|
||||||
g.writeln('\t\t.path = ${ctoslit(node.embed_file.rpath)},')
|
g.writeln('\t\t.path = ${ctoslit(node.embed_file.rpath)},')
|
||||||
if g.embed_file_is_prod_mode() {
|
if g.embed_file_is_prod_mode() {
|
||||||
|
@ -20,27 +66,36 @@ fn (mut g Gen) gen_embed_file_init(node ast.ComptimeCall) {
|
||||||
} else {
|
} else {
|
||||||
g.writeln('\t\t.apath = ${ctoslit(node.embed_file.apath)},')
|
g.writeln('\t\t.apath = ${ctoslit(node.embed_file.apath)},')
|
||||||
}
|
}
|
||||||
|
if g.embed_file_is_prod_mode() {
|
||||||
|
// use function generated in Gen.gen_embedded_data()
|
||||||
|
if node.embed_file.is_compressed {
|
||||||
|
g.writeln('\t\t.compression_type = ${ctoslit(node.embed_file.compression_type)},')
|
||||||
|
g.writeln('\t\t.compressed = v__embed_file__find_index_entry_by_path((voidptr)_v_embed_file_index, ${ctoslit(node.embed_file.rpath)}, ${ctoslit(node.embed_file.compression_type)})->data,')
|
||||||
|
g.writeln('\t\t.uncompressed = NULL,')
|
||||||
|
} else {
|
||||||
|
g.writeln('\t\t.uncompressed = v__embed_file__find_index_entry_by_path((voidptr)_v_embed_file_index, ${ctoslit(node.embed_file.rpath)}, ${ctoslit(node.embed_file.compression_type)})->data,')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g.writeln('\t\t.uncompressed = NULL,')
|
||||||
|
}
|
||||||
|
g.writeln('\t\t.free_compressed = 0,')
|
||||||
|
g.writeln('\t\t.free_uncompressed = 0,')
|
||||||
|
if g.embed_file_is_prod_mode() {
|
||||||
|
g.writeln('\t\t.len = $node.embed_file.len')
|
||||||
|
} else {
|
||||||
file_size := os.file_size(node.embed_file.apath)
|
file_size := os.file_size(node.embed_file.apath)
|
||||||
if file_size > 5242880 {
|
if file_size > 5242880 {
|
||||||
eprintln('Warning: embedding of files >= ~5MB is currently not supported')
|
eprintln('Warning: embedding of files >= ~5MB is currently not supported')
|
||||||
}
|
}
|
||||||
if g.embed_file_is_prod_mode() {
|
|
||||||
// Use function generated in Gen.gen_embedded_data()
|
|
||||||
g.writeln('\t\t.compressed = v__embed_file__find_index_entry_by_path((voidptr)_v_embed_file_index, ${ctoslit(node.embed_file.rpath)})->data,')
|
|
||||||
}
|
|
||||||
g.writeln('\t\t.uncompressed = NULL,')
|
|
||||||
g.writeln('\t\t.free_compressed = 0,')
|
|
||||||
g.writeln('\t\t.free_uncompressed = 0,')
|
|
||||||
g.writeln('\t\t.len = $file_size')
|
g.writeln('\t\t.len = $file_size')
|
||||||
|
}
|
||||||
g.writeln('} // \$embed_file("$node.embed_file.apath")')
|
g.writeln('} // \$embed_file("$node.embed_file.apath")')
|
||||||
|
|
||||||
|
g.file.embedded_files << node.embed_file
|
||||||
}
|
}
|
||||||
|
|
||||||
// gen_embedded_data embeds data into the V target executable.
|
// gen_embedded_data embeds data into the V target executable.
|
||||||
fn (mut g Gen) gen_embedded_data() {
|
fn (mut g Gen) gen_embedded_data() {
|
||||||
/*
|
|
||||||
TODO implement compression.
|
|
||||||
See also the vlib/embed module where decompression should occur.
|
|
||||||
*/
|
|
||||||
/*
|
/*
|
||||||
TODO implement support for large files - right now the setup has problems
|
TODO implement support for large files - right now the setup has problems
|
||||||
// with even just 10 - 50 MB files - the problem is both in V and C compilers.
|
// with even just 10 - 50 MB files - the problem is both in V and C compilers.
|
||||||
|
@ -48,11 +103,10 @@ fn (mut g Gen) gen_embedded_data() {
|
||||||
// like the `rcc` tool in Qt?
|
// like the `rcc` tool in Qt?
|
||||||
*/
|
*/
|
||||||
for i, emfile in g.embedded_files {
|
for i, emfile in g.embedded_files {
|
||||||
fbytes := os.read_bytes(emfile.apath) or { panic('Error while embedding file: $err') }
|
g.embedded_data.write_string('static const unsigned char _v_embed_blob_$i[$emfile.bytes.len] = {\n ')
|
||||||
g.embedded_data.write_string('static const unsigned char _v_embed_blob_$i[$fbytes.len] = {\n ')
|
for j := 0; j < emfile.bytes.len; j++ {
|
||||||
for j := 0; j < fbytes.len; j++ {
|
b := emfile.bytes[j].hex()
|
||||||
b := fbytes[j].hex()
|
if j < emfile.bytes.len - 1 {
|
||||||
if j < fbytes.len - 1 {
|
|
||||||
g.embedded_data.write_string('0x$b,')
|
g.embedded_data.write_string('0x$b,')
|
||||||
} else {
|
} else {
|
||||||
g.embedded_data.write_string('0x$b')
|
g.embedded_data.write_string('0x$b')
|
||||||
|
@ -66,9 +120,9 @@ fn (mut g Gen) gen_embedded_data() {
|
||||||
g.embedded_data.writeln('')
|
g.embedded_data.writeln('')
|
||||||
g.embedded_data.writeln('const v__embed_file__EmbedFileIndexEntry _v_embed_file_index[] = {')
|
g.embedded_data.writeln('const v__embed_file__EmbedFileIndexEntry _v_embed_file_index[] = {')
|
||||||
for i, emfile in g.embedded_files {
|
for i, emfile in g.embedded_files {
|
||||||
g.embedded_data.writeln('\t{$i, { .str=(byteptr)("${cestring(emfile.rpath)}"), .len=$emfile.rpath.len, .is_lit=1 }, _v_embed_blob_$i},')
|
g.embedded_data.writeln('\t{$i, { .str=(byteptr)("${cestring(emfile.rpath)}"), .len=$emfile.rpath.len, .is_lit=1 }, { .str=(byteptr)("${cestring(emfile.compression_type)}"), .len=$emfile.compression_type.len, .is_lit=1 }, _v_embed_blob_$i},')
|
||||||
}
|
}
|
||||||
g.embedded_data.writeln('\t{-1, { .str=(byteptr)(""), .len=0, .is_lit=1 }, NULL}')
|
g.embedded_data.writeln('\t{-1, { .str=(byteptr)(""), .len=0, .is_lit=1 }, { .str=(byteptr)(""), .len=0, .is_lit=1 }, NULL}')
|
||||||
g.embedded_data.writeln('};')
|
g.embedded_data.writeln('};')
|
||||||
// see vlib/v/embed_file/embed_file.v, find_index_entry_by_id/2 and find_index_entry_by_path/2
|
// see vlib/v/embed_file/embed_file.v, find_index_entry_by_id/2 and find_index_entry_by_path/2
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,11 @@ struct v__embed_file__EmbedFileIndexEntry {
|
||||||
string v__embed_file__EmbedFileData_str(v__embed_file__EmbedFileData ed);
|
string v__embed_file__EmbedFileData_str(v__embed_file__EmbedFileData ed);
|
||||||
void v__embed_file__EmbedFileData_free(v__embed_file__EmbedFileData* ed);
|
void v__embed_file__EmbedFileData_free(v__embed_file__EmbedFileData* ed);
|
||||||
byte* v__embed_file__EmbedFileData_data(v__embed_file__EmbedFileData* ed);
|
byte* v__embed_file__EmbedFileData_data(v__embed_file__EmbedFileData* ed);
|
||||||
v__embed_file__EmbedFileIndexEntry* v__embed_file__find_index_entry_by_path(voidptr start, string path);
|
v__embed_file__EmbedFileIndexEntry* v__embed_file__find_index_entry_by_path(voidptr start, string path, string algo);
|
||||||
|
|
||||||
string v__embed_file__EmbedFileData_str(v__embed_file__EmbedFileData ed) {
|
string v__embed_file__EmbedFileData_str(v__embed_file__EmbedFileData ed) {
|
||||||
string v__embed_file__EmbedFileData_to_string(v__embed_file__EmbedFileData* original) {
|
string v__embed_file__EmbedFileData_to_string(v__embed_file__EmbedFileData* original) {
|
||||||
v__embed_file__EmbedFileIndexEntry* v__embed_file__find_index_entry_by_path(voidptr start, string path) {
|
v__embed_file__EmbedFileIndexEntry* v__embed_file__find_index_entry_by_path(voidptr start, string path, string algo) {
|
||||||
|
|
||||||
v__embed_file__EmbedFileData my_source = (v__embed_file__EmbedFileData){
|
v__embed_file__EmbedFileData my_source = (v__embed_file__EmbedFileData){
|
||||||
.path = _SLIT("embed.vv"),
|
.path = _SLIT("embed.vv"),
|
||||||
|
|
|
@ -5,8 +5,8 @@ static const unsigned char _v_embed_blob_0[138] = {
|
||||||
0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x20,0x7b,0x0a,0x09,0x6d,0x75,0x74,
|
0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x20,0x7b,0x0a,0x09,0x6d,0x75,0x74,
|
||||||
|
|
||||||
const v__embed_file__EmbedFileIndexEntry _v_embed_file_index[] = {
|
const v__embed_file__EmbedFileIndexEntry _v_embed_file_index[] = {
|
||||||
{0, { .str=(byteptr)("embed.vv"), .len=8, .is_lit=1 }, _v_embed_blob_0},
|
{0, { .str=(byteptr)("embed.vv"), .len=8, .is_lit=1 }, { .str=(byteptr)("none"), .len=4, .is_lit=1 }, _v_embed_blob_0},
|
||||||
{-1, { .str=(byteptr)(""), .len=0, .is_lit=1 }, NULL}
|
{-1, { .str=(byteptr)(""), .len=0, .is_lit=1 }, { .str=(byteptr)(""), .len=0, .is_lit=1 }, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct v__embed_file__EmbedFileData v__embed_file__EmbedFileData;
|
typedef struct v__embed_file__EmbedFileData v__embed_file__EmbedFileData;
|
||||||
|
@ -18,15 +18,15 @@ struct v__embed_file__EmbedFileIndexEntry {
|
||||||
string v__embed_file__EmbedFileData_str(v__embed_file__EmbedFileData ed);
|
string v__embed_file__EmbedFileData_str(v__embed_file__EmbedFileData ed);
|
||||||
void v__embed_file__EmbedFileData_free(v__embed_file__EmbedFileData* ed);
|
void v__embed_file__EmbedFileData_free(v__embed_file__EmbedFileData* ed);
|
||||||
byte* v__embed_file__EmbedFileData_data(v__embed_file__EmbedFileData* ed);
|
byte* v__embed_file__EmbedFileData_data(v__embed_file__EmbedFileData* ed);
|
||||||
v__embed_file__EmbedFileIndexEntry* v__embed_file__find_index_entry_by_path(voidptr start, string path);
|
v__embed_file__EmbedFileIndexEntry* v__embed_file__find_index_entry_by_path(voidptr start, string path, string algo);
|
||||||
|
|
||||||
string v__embed_file__EmbedFileData_str(v__embed_file__EmbedFileData ed) {
|
string v__embed_file__EmbedFileData_str(v__embed_file__EmbedFileData ed) {
|
||||||
string v__embed_file__EmbedFileData_to_string(v__embed_file__EmbedFileData* original) {
|
string v__embed_file__EmbedFileData_to_string(v__embed_file__EmbedFileData* original) {
|
||||||
v__embed_file__EmbedFileIndexEntry* v__embed_file__find_index_entry_by_path(voidptr start, string path) {
|
v__embed_file__EmbedFileIndexEntry* v__embed_file__find_index_entry_by_path(voidptr start, string path, string algo) {
|
||||||
|
|
||||||
v__embed_file__EmbedFileData my_source = (v__embed_file__EmbedFileData){
|
v__embed_file__EmbedFileData my_source = (v__embed_file__EmbedFileData){
|
||||||
.path = _SLIT("embed.vv"),
|
.path = _SLIT("embed.vv"),
|
||||||
.apath = _SLIT(""),
|
.apath = _SLIT(""),
|
||||||
.compressed = v__embed_file__find_index_entry_by_path((voidptr)_v_embed_file_index, _SLIT("embed.vv"))->data,
|
.uncompressed = v__embed_file__find_index_entry_by_path((voidptr)_v_embed_file_index, _SLIT("embed.vv"), _SLIT("none"))->data,
|
||||||
|
|
||||||
// Initializations for module v.embed_file :
|
// Initializations for module v.embed_file :
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
fn main() {
|
||||||
|
mut my_source := $embed_file('embed.vv')
|
||||||
|
assert my_source.len > 0
|
||||||
|
s := my_source.to_string()
|
||||||
|
assert s.len > 0
|
||||||
|
print(s)
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
{0, { .str=(byteptr)("embed.vv"), .len=8, .is_lit=1 }, { .str=(byteptr)("zlib"), .len=4, .is_lit=1 }, _v_embed_blob_0},
|
||||||
|
{1, { .str=(byteptr)("embed.vv"), .len=8, .is_lit=1 }, { .str=(byteptr)("none"), .len=4, .is_lit=1 }, _v_embed_blob_1},
|
||||||
|
|
||||||
|
VV_LOCAL_SYMBOL void v__preludes__embed_file__zlib__init(void);
|
||||||
|
VV_LOCAL_SYMBOL Option_Array_byte v__preludes__embed_file__zlib__ZLibDecoder_decompress(v__preludes__embed_file__zlib__ZLibDecoder _d1, Array_byte data) {
|
||||||
|
= compress__zlib__decompress(data);
|
||||||
|
|
||||||
|
.compressed = v__embed_file__find_index_entry_by_path((voidptr)_v_embed_file_index, _SLIT("embed.vv"), _SLIT("zlib"))->data,
|
||||||
|
.compression_type = _SLIT("zlib")
|
||||||
|
.uncompressed = v__embed_file__find_index_entry_by_path((voidptr)_v_embed_file_index, _SLIT("embed.vv"), _SLIT("none"))->data,
|
|
@ -0,0 +1,23 @@
|
||||||
|
fn main() {
|
||||||
|
mut my_source := $embed_file('embed.vv')
|
||||||
|
assert my_source.len > 0
|
||||||
|
s := my_source.to_string()
|
||||||
|
assert s.len > 0
|
||||||
|
print(s)
|
||||||
|
}
|
||||||
|
--------------------------------------
|
||||||
|
fn main() {
|
||||||
|
mut my_source := $embed_file('embed.vv')
|
||||||
|
assert my_source.len > 0
|
||||||
|
s := my_source.to_string()
|
||||||
|
assert s.len > 0
|
||||||
|
print(s)
|
||||||
|
}
|
||||||
|
--------------------------------------
|
||||||
|
fn main() {
|
||||||
|
mut my_source := $embed_file('embed.vv')
|
||||||
|
assert my_source.len > 0
|
||||||
|
s := my_source.to_string()
|
||||||
|
assert s.len > 0
|
||||||
|
print(s)
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
// vtest vflags: -prod
|
||||||
|
// NB: asserts are stripped with -prod => instead use plain if{panic()}
|
||||||
|
fn main() {
|
||||||
|
mut my_source := $embed_file('embed.vv', .zlib)
|
||||||
|
mut my_source_2 := $embed_file('embed.vv', .zlib)
|
||||||
|
if my_source.len <= 0 {
|
||||||
|
panic('my_source.len <= 0')
|
||||||
|
}
|
||||||
|
s := my_source.to_string()
|
||||||
|
if s.len <= 0 {
|
||||||
|
panic('s.len <= 0')
|
||||||
|
}
|
||||||
|
print(s)
|
||||||
|
|
||||||
|
println('--------------------------------------')
|
||||||
|
print( my_source_2.to_string() )
|
||||||
|
println('--------------------------------------')
|
||||||
|
|
||||||
|
mut my_uncompressed_source := $embed_file('embed.vv')
|
||||||
|
if my_uncompressed_source.len <= 0 {
|
||||||
|
panic('my_uncompressed_source.len <= 0')
|
||||||
|
}
|
||||||
|
us :=my_uncompressed_source.to_string()
|
||||||
|
if us.len <= 0 {
|
||||||
|
panic('us.len <= 0')
|
||||||
|
}
|
||||||
|
print(us)
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
if us != s {
|
||||||
|
println('us != s')
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
mut my_uncompressed_source_2 := $embed_file('embed.vv')
|
||||||
|
if my_uncompressed_source.len != my_uncompressed_source_2.len {
|
||||||
|
panic('my_uncompressed_source.len != my_uncompressed_source_2.len')
|
||||||
|
}
|
||||||
|
|
||||||
|
if my_source.len != my_source_2.len {
|
||||||
|
panic('my_source.len != my_source_2.len')
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
#define _VPROD (1)
|
||||||
|
|
||||||
|
// V embedded data:
|
||||||
|
static const unsigned char _v_embed_blob_0[121] = {
|
||||||
|
0x78,0x01,0x05,0x40,0x4b,0x0a,0x83,0x30,0x10,0x5d,0x67,0x4e,0xf1,0x16,0x05,0x93,
|
||||||
|
|
||||||
|
const v__embed_file__EmbedFileIndexEntry _v_embed_file_index[] = {
|
||||||
|
{0, { .str=(byteptr)("embed.vv"), .len=8, .is_lit=1 }, { .str=(byteptr)("zlib"), .len=4, .is_lit=1 }, _v_embed_blob_0},
|
||||||
|
{-1, { .str=(byteptr)(""), .len=0, .is_lit=1 }, { .str=(byteptr)(""), .len=0, .is_lit=1 }, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct v__embed_file__EmbedFileData v__embed_file__EmbedFileData;
|
||||||
|
typedef struct v__embed_file__EmbedFileIndexEntry v__embed_file__EmbedFileIndexEntry;
|
||||||
|
|
||||||
|
struct v__embed_file__EmbedFileData {
|
||||||
|
struct v__embed_file__EmbedFileIndexEntry {
|
||||||
|
|
||||||
|
string v__embed_file__EmbedFileData_str(v__embed_file__EmbedFileData ed);
|
||||||
|
void v__embed_file__EmbedFileData_free(v__embed_file__EmbedFileData* ed);
|
||||||
|
byte* v__embed_file__EmbedFileData_data(v__embed_file__EmbedFileData* ed);
|
||||||
|
v__embed_file__EmbedFileIndexEntry* v__embed_file__find_index_entry_by_path(voidptr start, string path, string algo);
|
||||||
|
|
||||||
|
string v__embed_file__EmbedFileData_str(v__embed_file__EmbedFileData ed) {
|
||||||
|
string v__embed_file__EmbedFileData_to_string(v__embed_file__EmbedFileData* original) {
|
||||||
|
v__embed_file__EmbedFileIndexEntry* v__embed_file__find_index_entry_by_path(voidptr start, string path, string algo) {
|
||||||
|
|
||||||
|
v__embed_file__EmbedFileData my_source = (v__embed_file__EmbedFileData){
|
||||||
|
.path = _SLIT("embed.vv"),
|
||||||
|
.apath = _SLIT(""),
|
||||||
|
.compressed = v__embed_file__find_index_entry_by_path((voidptr)_v_embed_file_index, _SLIT("embed.vv"), _SLIT("zlib"))->data,
|
||||||
|
|
||||||
|
// Initializations for module v.embed_file :
|
|
@ -0,0 +1,7 @@
|
||||||
|
fn main() {
|
||||||
|
mut my_source := $embed_file('embed.vv')
|
||||||
|
assert my_source.len > 0
|
||||||
|
s := my_source.to_string()
|
||||||
|
assert s.len > 0
|
||||||
|
print(s)
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
// vtest vflags: -prod
|
||||||
|
fn main() {
|
||||||
|
mut my_source := $embed_file('embed.vv', .zlib)
|
||||||
|
assert my_source.len > 0
|
||||||
|
s := my_source.to_string()
|
||||||
|
assert s.len > 0
|
||||||
|
print(s)
|
||||||
|
}
|
|
@ -95,6 +95,14 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall {
|
||||||
if !is_html {
|
if !is_html {
|
||||||
p.check(.string)
|
p.check(.string)
|
||||||
}
|
}
|
||||||
|
mut embed_compression_type := 'none'
|
||||||
|
if is_embed_file {
|
||||||
|
if p.tok.kind == .comma {
|
||||||
|
p.check(.comma)
|
||||||
|
p.check(.dot)
|
||||||
|
embed_compression_type = p.check_name()
|
||||||
|
}
|
||||||
|
}
|
||||||
p.check(.rpar)
|
p.check(.rpar)
|
||||||
// $embed_file('/path/to/file')
|
// $embed_file('/path/to/file')
|
||||||
if is_embed_file {
|
if is_embed_file {
|
||||||
|
@ -126,12 +134,17 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.register_auto_import('v.preludes.embed_file')
|
p.register_auto_import('v.preludes.embed_file')
|
||||||
|
if embed_compression_type == 'zlib'
|
||||||
|
&& (p.pref.is_prod || 'debug_embed_file_in_prod' in p.pref.compile_defines) {
|
||||||
|
p.register_auto_import('v.preludes.embed_file.zlib')
|
||||||
|
}
|
||||||
return ast.ComptimeCall{
|
return ast.ComptimeCall{
|
||||||
scope: 0
|
scope: 0
|
||||||
is_embed: true
|
is_embed: true
|
||||||
embed_file: ast.EmbeddedFile{
|
embed_file: ast.EmbeddedFile{
|
||||||
rpath: literal_string_param
|
rpath: literal_string_param
|
||||||
apath: epath
|
apath: epath
|
||||||
|
compression_type: embed_compression_type
|
||||||
}
|
}
|
||||||
pos: start_pos.extend(p.prev_tok.position())
|
pos: start_pos.extend(p.prev_tok.position())
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
module zlib
|
||||||
|
|
||||||
|
import compress.zlib
|
||||||
|
import v.embed_file
|
||||||
|
|
||||||
|
struct ZLibDecoder {}
|
||||||
|
|
||||||
|
fn (_ ZLibDecoder) decompress(data []byte) ?[]byte {
|
||||||
|
return zlib.decompress(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init() {
|
||||||
|
embed_file.register_decoder('zlib', embed_file.Decoder(ZLibDecoder{}))
|
||||||
|
}
|
Loading…
Reference in New Issue