all: support `$embed_file('embed.vv', .zlib)` (#12654)

pull/12723/head
Leo Developer 2021-12-04 18:43:19 +01:00 committed by GitHub
parent 0f50ac3260
commit ace63594bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 411 additions and 72 deletions

View File

@ -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)
}
}

View File

@ -20,6 +20,7 @@ const (
'build-vbinaries', 'build-vbinaries',
'check-md', 'check-md',
'complete', 'complete',
'compress',
'doc', 'doc',
'doctor', 'doctor',
'fmt', 'fmt',

View File

@ -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

View File

@ -646,8 +646,14 @@ pub mut:
pub struct EmbeddedFile { 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.
@ -1542,8 +1548,7 @@ pub:
is_vweb bool is_vweb bool
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 {

View File

@ -20,20 +20,21 @@ const int_min = int(0x80000000)
const int_max = int(0x7FFFFFFF) const int_max = int(0x7FFFFFFF)
const ( 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_if_compilers = ['gcc', 'tinyc', 'clang', 'mingw', 'msvc', 'cplusplus'] valid_comptime_compression_types = ['none', 'zlib']
valid_comptime_if_platforms = ['amd64', 'i386', 'aarch64', 'arm64', 'arm32', 'rv64', 'rv32'] valid_comptime_if_compilers = ['gcc', 'tinyc', 'clang', 'mingw', 'msvc', 'cplusplus']
valid_comptime_if_cpu_features = ['x64', 'x32', 'little_endian', 'big_endian'] valid_comptime_if_platforms = ['amd64', 'i386', 'aarch64', 'arm64', 'arm32', 'rv64', 'rv32']
valid_comptime_if_other = ['js', 'debug', 'prod', 'test', 'glibc', 'prealloc', valid_comptime_if_cpu_features = ['x64', 'x32', 'little_endian', 'big_endian']
valid_comptime_if_other = ['js', 'debug', 'prod', 'test', 'glibc', 'prealloc',
'no_bounds_checking', 'freestanding', 'threads', 'js_node', 'js_browser', 'js_freestanding'] 'no_bounds_checking', 'freestanding', 'threads', 'js_node', 'js_browser', 'js_freestanding']
valid_comptime_not_user_defined = all_valid_comptime_idents() valid_comptime_not_user_defined = all_valid_comptime_idents()
array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice',
'sort', 'contains', 'index', 'wait', 'any', 'all', 'first', 'last', 'pop'] 'sort', 'contains', 'index', 'wait', 'any', 'all', 'first', 'last', 'pop']
reserved_type_names = ['bool', 'char', 'i8', 'i16', 'int', 'i64', 'byte', 'u16', reserved_type_names = ['bool', 'char', 'i8', 'i16', 'int', 'i64', 'byte', 'u16',
'u32', 'u64', 'f32', 'f64', 'map', 'string', 'rune'] 'u32', 'u64', 'f32', 'f64', 'map', 'string', 'rune']
vroot_is_deprecated_message = '@VROOT is deprecated, use @VMODROOT or @VEXEROOT instead' vroot_is_deprecated_message = '@VROOT is deprecated, use @VMODROOT or @VEXEROOT instead'
) )
fn all_valid_comptime_idents() []string { fn all_valid_comptime_idents() []string {
@ -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 {

View File

@ -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 | }

View File

@ -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())
}

View File

@ -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
}

View File

@ -9,7 +9,8 @@ pub const (
// https://github.com/vlang/rfcs/blob/master/embedding_resources.md // https://github.com/vlang/rfcs/blob/master/embedding_resources.md
// 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,25 +61,31 @@ 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
} else {
mut path := os.resource_abs_path(ed.path)
if !os.is_file(path) {
path = ed.apath
if !os.is_file(path) {
panic('EmbedFileData error: files "$ed.path" and "$ed.apath" do not exist')
}
}
bytes := os.read_bytes(path) or {
panic('EmbedFileData error: "$path" could not be read: $err')
}
ed.uncompressed = bytes.data
ed.free_uncompressed = true
} }
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 {
mut path := os.resource_abs_path(ed.path)
if !os.is_file(path) {
path = ed.apath
if !os.is_file(path) {
panic('EmbedFileData error: files "$ed.path" and "$ed.apath" do not exist')
}
}
bytes := os.read_bytes(path) or {
panic('EmbedFileData error: "$path" could not be read: $err')
}
ed.uncompressed = bytes.data
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
} }

View File

@ -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)

View File

@ -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' {

View File

@ -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 {
eprintln('> ALL lines:') if show_compilation_output {
eprintln(compilation.output) eprintln('> ALL lines:')
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
} }

View File

@ -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)},')
} }
file_size := os.file_size(node.embed_file.apath)
if file_size > 5242880 {
eprintln('Warning: embedding of files >= ~5MB is currently not supported')
}
if g.embed_file_is_prod_mode() { if g.embed_file_is_prod_mode() {
// Use function generated in Gen.gen_embedded_data() // 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,') 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.uncompressed = NULL,')
g.writeln('\t\t.free_compressed = 0,') g.writeln('\t\t.free_compressed = 0,')
g.writeln('\t\t.free_uncompressed = 0,') g.writeln('\t\t.free_uncompressed = 0,')
g.writeln('\t\t.len = $file_size') 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)
if file_size > 5242880 {
eprintln('Warning: embedding of files >= ~5MB is currently not supported')
}
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
} }

View File

@ -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"),

View File

@ -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 :

View 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)
}

View File

@ -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,

View File

@ -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)
}

View File

@ -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')
}
}

View File

@ -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 :

View 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)
}

View File

@ -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)
}

View File

@ -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())
} }

View File

@ -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{}))
}